From f677a9e2672665f4eb3dd4111c07ee8f1f954262 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Wed, 9 Oct 2013 08:12:26 +0000 Subject: [PATCH 01/18] Introduce centralized [X]dialog(1) exit codes to `dialog.subr' and make-use throughout the bsdconfig(8) code. While we're here, add an explicit argument to lvalue-seeking invocations of "return" that previously had no argument. Also, consolidate a single instance of double-newline and remove some comments that are no longer required (given increased readability with new exit codes). Approved by: re (glebius) --- usr.sbin/bsdconfig/bsdconfig | 7 +- usr.sbin/bsdconfig/console/ttys | 2 +- usr.sbin/bsdconfig/mouse/enable | 2 +- usr.sbin/bsdconfig/networking/devices | 2 +- .../bsdconfig/networking/share/device.subr | 18 +++-- .../bsdconfig/networking/share/hostname.subr | 4 +- .../bsdconfig/networking/share/ipaddr.subr | 10 +-- .../bsdconfig/networking/share/media.subr | 8 +-- .../bsdconfig/networking/share/netmask.subr | 8 +-- .../bsdconfig/networking/share/resolv.subr | 19 ++--- .../bsdconfig/networking/share/routing.subr | 10 +-- .../bsdconfig/password/share/password.subr | 2 +- usr.sbin/bsdconfig/security/kern_securelevel | 5 +- usr.sbin/bsdconfig/security/security | 2 +- usr.sbin/bsdconfig/share/common.subr | 7 +- usr.sbin/bsdconfig/share/device.subr | 12 ++-- usr.sbin/bsdconfig/share/dialog.subr | 12 ++++ usr.sbin/bsdconfig/share/media/any.subr | 5 +- usr.sbin/bsdconfig/share/media/cdrom.subr | 2 +- usr.sbin/bsdconfig/share/media/common.subr | 2 +- usr.sbin/bsdconfig/share/media/dos.subr | 2 +- usr.sbin/bsdconfig/share/media/floppy.subr | 2 +- usr.sbin/bsdconfig/share/media/ftp.subr | 6 +- usr.sbin/bsdconfig/share/media/http.subr | 4 +- usr.sbin/bsdconfig/share/media/nfs.subr | 2 +- usr.sbin/bsdconfig/share/media/options.subr | 5 +- usr.sbin/bsdconfig/share/media/tcpip.subr | 39 +++++------ usr.sbin/bsdconfig/share/media/ufs.subr | 2 +- usr.sbin/bsdconfig/share/media/usb.subr | 2 +- usr.sbin/bsdconfig/share/mustberoot.subr | 8 +-- .../bsdconfig/share/packages/packages.subr | 22 +++--- usr.sbin/bsdconfig/startup/misc | 6 +- usr.sbin/bsdconfig/startup/rcconf | 6 +- usr.sbin/bsdconfig/startup/rcdelete | 8 +-- usr.sbin/bsdconfig/startup/rcvar | 2 +- usr.sbin/bsdconfig/startup/share/rcconf.subr | 6 +- usr.sbin/bsdconfig/startup/share/rcedit.subr | 4 +- usr.sbin/bsdconfig/timezone/timezone | 12 ++-- usr.sbin/bsdconfig/usermgmt/groupdel | 2 +- usr.sbin/bsdconfig/usermgmt/groupedit | 2 +- usr.sbin/bsdconfig/usermgmt/groupinput | 18 +++-- .../bsdconfig/usermgmt/share/group_input.subr | 26 +++---- .../bsdconfig/usermgmt/share/user_input.subr | 70 +++++++++---------- usr.sbin/bsdconfig/usermgmt/userdel | 2 +- usr.sbin/bsdconfig/usermgmt/useredit | 2 +- usr.sbin/bsdconfig/usermgmt/userinput | 8 +-- usr.sbin/bsdconfig/usermgmt/usermgmt | 7 +- 47 files changed, 208 insertions(+), 206 deletions(-) diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index b23e083bcf6..2c0d513e3cd 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -300,7 +300,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } @@ -396,11 +396,10 @@ while :; do f_dialog_menutag_fetch mtag f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$BSDCONFIG_HELPFILE" continue - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then f_die fi diff --git a/usr.sbin/bsdconfig/console/ttys b/usr.sbin/bsdconfig/console/ttys index c0f3ee8fa8b..80cc66847e8 100755 --- a/usr.sbin/bsdconfig/console/ttys +++ b/usr.sbin/bsdconfig/console/ttys @@ -92,7 +92,7 @@ dialog_menu_main() local retval=$? f_dialog_menutag_store -s "$menu_choice" - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then local item item=$( eval f_dialog_menutag2item \ \"\$menu_choice\" $menu_list ) diff --git a/usr.sbin/bsdconfig/mouse/enable b/usr.sbin/bsdconfig/mouse/enable index 695eac08d9c..106faec2d46 100755 --- a/usr.sbin/bsdconfig/mouse/enable +++ b/usr.sbin/bsdconfig/mouse/enable @@ -102,7 +102,7 @@ f_dialog_title_restore # Stop the mouse daemon # f_quietly vidcontrol -m off -if [ $retval -eq $SUCCESS ]; then +if [ $retval -eq $DIALOG_OK ]; then f_sysrc_set moused_enable "YES" || f_die ln -fs /dev/sysmouse /dev/mouse || f_die # backwards compat else diff --git a/usr.sbin/bsdconfig/networking/devices b/usr.sbin/bsdconfig/networking/devices index 00a50c51535..9d65c5100df 100755 --- a/usr.sbin/bsdconfig/networking/devices +++ b/usr.sbin/bsdconfig/networking/devices @@ -148,7 +148,7 @@ while :; do "$interface" "$_ipaddr" "$_netmask" "$_options" $dhcp # Return to root menu if above returns success - [ $? -eq $SUCCESS ] && break + [ $? -eq $DIALOG_OK ] && break done exit $SUCCESS diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr index a0ca5cc313f..f4198c13796 100644 --- a/usr.sbin/bsdconfig/networking/share/device.subr +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -125,7 +125,7 @@ f_dialog_menu_netdev() ) if [ ! "$interfaces" ]; then f_show_msg "$msg_no_network_interfaces" - return $FAILURE + return $DIALOG_CANCEL fi # @@ -242,12 +242,10 @@ f_dialog_menu_netdev_edit() local retval=$? f_dialog_data_sanitize tag - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$TCP_HELPFILE" continue - elif [ $retval -ne $SUCCESS ]; then - # "Cancel" was chosen (-1) or ESC was pressed (255) + elif [ $retval -ne $DIALOG_OK ]; then return $retval else # Only update default-item on success @@ -298,7 +296,7 @@ f_dialog_menu_netdev_edit() ) retval=$? trap 'interrupt' SIGINT - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then dhcp=1 ipaddr=$( f_ifconfig_inet $interface ) netmask=$( f_ifconfig_netmask $interface ) @@ -312,11 +310,11 @@ f_dialog_menu_netdev_edit() fi ;; 3\ *) f_dialog_input_ipaddr "$interface" "$ipaddr" - [ $? -eq $SUCCESS ] && dhcp= ;; + [ $? -eq $DIALOG_OK ] && dhcp= ;; 4\ *) f_dialog_input_netmask "$interface" "$netmask" - [ $? -eq $SUCCESS -a "$_netmask" ] && dhcp= ;; + [ $? -eq $DIALOG_OK -a "$_netmask" ] && dhcp= ;; 5\ *) f_dialog_menu_media_options "$interface" "$options" - [ $? -eq $SUCCESS ] && dhcp= ;; + [ $? -eq $DIALOG_OK ] && dhcp= ;; esac done @@ -383,7 +381,7 @@ f_dialog_menu_netdev_edit() fi fi - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/networking/share/hostname.subr b/usr.sbin/bsdconfig/networking/share/hostname.subr index 05f03154c67..ab47494bddb 100644 --- a/usr.sbin/bsdconfig/networking/share/hostname.subr +++ b/usr.sbin/bsdconfig/networking/share/hostname.subr @@ -110,7 +110,7 @@ f_dialog_input_hostname() # while :; do f_dialog_input hostname "$msg" "$hostname" \ - "$hline_alnum_punc_tab_enter" || return + "$hline_alnum_punc_tab_enter" || return $? # Taint-check the user's input f_dialog_validate_hostname "$hostname" && break done @@ -150,7 +150,7 @@ f_dialog_input_hostname() fi fi - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/networking/share/ipaddr.subr b/usr.sbin/bsdconfig/networking/share/ipaddr.subr index 9c51f592a90..f67713a25eb 100644 --- a/usr.sbin/bsdconfig/networking/share/ipaddr.subr +++ b/usr.sbin/bsdconfig/networking/share/ipaddr.subr @@ -147,7 +147,7 @@ f_dialog_input_ipaddr() local setting="$( printf "$msg_current_ipaddr" \ "$interface" "$_ipaddr" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )" @@ -163,11 +163,11 @@ f_dialog_input_ipaddr() # - User has not made any changes to the given value # f_dialog_input _input "$msg" "$_ipaddr" \ - "$hline_num_punc_tab_enter" || return - [ "$_ipaddr" = "$_input" ] && return $FAILURE + "$hline_num_punc_tab_enter" || return $? + [ "$_ipaddr" = "$_input" ] && return $DIALOG_CANCEL # Return success if NULL value was entered - [ "$_input" ] || return $SUCCESS + [ "$_input" ] || return $DIALOG_OK # Take only the first "word" of the user's input _ipaddr="$_input" @@ -208,7 +208,7 @@ f_dialog_input_ipaddr() ipaddr="$_ipaddr" [ "$_netmask" ] && netmask="$_netmask" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/networking/share/media.subr b/usr.sbin/bsdconfig/networking/share/media.subr index c6efa57a945..d4283c12fe3 100644 --- a/usr.sbin/bsdconfig/networking/share/media.subr +++ b/usr.sbin/bsdconfig/networking/share/media.subr @@ -118,7 +118,7 @@ f_dialog_input_options() local setting="$( printf "$msg_current_options" \ "$interface" "$options" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi local msg="$( printf "$msg_please_enter_mediaopts" "$interface" )" @@ -138,7 +138,7 @@ f_dialog_input_options() local retval=$? f_dialog_line_sanitize _options - [ $retval -eq $SUCCESS ] && options="$_options" + [ $retval -eq $DIALOG_OK ] && options="$_options" return $retval } @@ -165,7 +165,7 @@ f_dialog_menu_media_options() local setting="$( printf "$msg_current_options" \ "$interface" "$_options" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi # @@ -219,7 +219,7 @@ f_dialog_menu_media_options() local retval=$? f_dialog_data_sanitize tag - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then options=$( eval f_dialog_menutag2item \"\$tag\" \ $supported_media ) case "$options" in diff --git a/usr.sbin/bsdconfig/networking/share/netmask.subr b/usr.sbin/bsdconfig/networking/share/netmask.subr index 845bf7e0c26..ebd91fc3045 100644 --- a/usr.sbin/bsdconfig/networking/share/netmask.subr +++ b/usr.sbin/bsdconfig/networking/share/netmask.subr @@ -97,7 +97,7 @@ f_dialog_input_netmask() local setting="$( printf "$msg_current_subnet" \ "$interface" "$_netmask" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi # @@ -111,11 +111,11 @@ f_dialog_input_netmask() # - User has not made any changes to the given value # f_dialog_input _input "$msg" "$_netmask" \ - "$hline_num_punc_tab_enter" || return - [ "$_netmask" = "$_input" ] && return $FAILURE + "$hline_num_punc_tab_enter" || return $? + [ "$_netmask" = "$_input" ] && return $DIALOG_CANCEL # Return success if NULL value was entered - [ "$_input" ] || return $SUCCESS + [ "$_input" ] || return $DIALOG_OK # Take only the first "word" of the user's input _netmask="$_input" diff --git a/usr.sbin/bsdconfig/networking/share/resolv.subr b/usr.sbin/bsdconfig/networking/share/resolv.subr index ac281fc9ba5..dfeac34e694 100644 --- a/usr.sbin/bsdconfig/networking/share/resolv.subr +++ b/usr.sbin/bsdconfig/networking/share/resolv.subr @@ -195,7 +195,7 @@ f_dialog_resolv_conf_update() # update with our new `domain' and `search' directives. # local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE + [ "$tmpfile" ] || return $DIALOG_CANCEL # # Fixup permissions and ownership (mktemp(1) creates the @@ -235,7 +235,8 @@ f_dialog_resolv_conf_update() # Write the temporary file contents and move the temporary # file into place. # - echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE + echo "$new_contents" | tail -r > "$tmpfile" || + return $DIALOG_CANCEL f_quietly mv "$tmpfile" "$RESOLV_CONF" fi @@ -293,8 +294,8 @@ f_dialog_input_nameserver() # # Perform sanity checks # - f_isinteger "$index" || return $FAILURE - [ $index -ge 0 ] || return $FAILURE + f_isinteger "$index" || return $DIALOG_CANCEL + [ $index -ge 0 ] || return $DIALOG_CANCEL local msg if [ $index -gt 0 ]; then @@ -312,7 +313,7 @@ f_dialog_input_nameserver() # while :; do f_dialog_input new_ns "$msg" "$ns" \ - "$hline_num_punc_tab_enter" || return + "$hline_num_punc_tab_enter" || return $? # Take only the first "word" of the user's input new_ns="${new_ns%%[$IFS]*}" @@ -331,7 +332,7 @@ f_dialog_input_nameserver() if [ $index -eq "0" -a "$new_ns" ]; then f_dialog_info "$msg_saving_nameserver" printf "nameserver\t%s\n" "$new_ns" >> "$RESOLV_CONF" - return $SUCCESS + return $DIALOG_OK elif [ $index -gt 0 -a "$old_ns" != "$new_ns" ]; then if [ "$new_ns" ]; then msg="$msg_saving_nameserver_existing" @@ -344,7 +345,7 @@ f_dialog_input_nameserver() # Create a new temporary file to write our new resolv.conf(5) # local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE + [ "$tmpfile" ] || return $DIALOG_CANCEL # # Quietly fixup permissions and ownership @@ -381,7 +382,7 @@ f_dialog_input_nameserver() # Write the temporary file contents and move the temporary # file into place. # - echo "$new_contents" > "$tmpfile" || return $FAILURE + echo "$new_contents" > "$tmpfile" || return $DIALOG_CANCEL f_quietly mv "$tmpfile" "$RESOLV_CONF" fi } @@ -452,7 +453,7 @@ f_dialog_menu_nameservers() f_dialog_data_sanitize tag # Return if "Cancel" was chosen (-1) or ESC was pressed (255) - if [ $retval -ne $SUCCESS ]; then + if [ $retval -ne $DIALOG_OK ]; then return $retval else # Only update default-item on success diff --git a/usr.sbin/bsdconfig/networking/share/routing.subr b/usr.sbin/bsdconfig/networking/share/routing.subr index 3af9f36df32..d0b8927b331 100644 --- a/usr.sbin/bsdconfig/networking/share/routing.subr +++ b/usr.sbin/bsdconfig/networking/share/routing.subr @@ -75,7 +75,7 @@ f_dialog_input_defaultrouter() local setting="$( printf "$msg_current_default_router" \ "$defaultrouter" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi # @@ -87,8 +87,8 @@ f_dialog_input_defaultrouter() "$msg_please_enter_default_router" \ "$defaultrouter" "$hline_num_punc_tab_enter" retval=$? - [ "$defaultrouter" ] || return $SUCCESS - [ $retval -eq $SUCCESS ] || return $retval + [ "$defaultrouter" ] || return $DIALOG_OK + [ $retval -eq $DIALOG_OK ] || return $retval # Taint-check the user's input f_dialog_validate_ipaddr "$defaultrouter" && break @@ -112,7 +112,7 @@ f_dialog_input_defaultrouter() f_dialog_clear f_yesno "$msg_activate_default_router" \ "$( f_route_get_default )" "$defaultrouter" - if [ $? -eq $SUCCESS ]; then + if [ $? -eq $DIALOG_OK ]; then local err # Apply the default router/gateway @@ -120,7 +120,7 @@ f_dialog_input_defaultrouter() err=$( route add default "$defaultrouter" 2>&1 ) if [ $? -ne $SUCCESS ]; then f_dialog_msgbox "$err" - return $FAILURE + return $DIALOG_CANCEL fi fi fi diff --git a/usr.sbin/bsdconfig/password/share/password.subr b/usr.sbin/bsdconfig/password/share/password.subr index 05db7f1d882..22803394fc3 100644 --- a/usr.sbin/bsdconfig/password/share/password.subr +++ b/usr.sbin/bsdconfig/password/share/password.subr @@ -114,7 +114,7 @@ f_dialog_input_password() break done - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/security/kern_securelevel b/usr.sbin/bsdconfig/security/kern_securelevel index fd1c91c9882..cce2ef0cdf6 100755 --- a/usr.sbin/bsdconfig/security/kern_securelevel +++ b/usr.sbin/bsdconfig/security/kern_securelevel @@ -131,11 +131,10 @@ while :; do retval=$? f_dialog_menutag_fetch mtag - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$SECURELEVEL_HELPFILE" continue - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then f_die fi diff --git a/usr.sbin/bsdconfig/security/security b/usr.sbin/bsdconfig/security/security index ca19a26aaf1..72489827d18 100755 --- a/usr.sbin/bsdconfig/security/security +++ b/usr.sbin/bsdconfig/security/security @@ -123,7 +123,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 985bf6b1c1f..d087ccacb1d 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -560,18 +560,19 @@ f_index_file() if [ "$lang" ]; then awk -v keyword="$keyword" "$f_index_file_awk" \ - $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX.$lang && return + $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX.$lang && + return $SUCCESS # No match, fall-thru to non-i18n sources fi awk -v keyword="$keyword" "$f_index_file_awk" \ - $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX && return + $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX && return $SUCCESS # No match? Fall-thru to `local' libexec sources (add-on modules) [ "$BSDCFG_LOCAL_LIBE" ] || return $FAILURE if [ "$lang" ]; then awk -v keyword="$keyword" "$f_index_file_awk" \ - $BSDCFG_LOCAL_LIBE/*/INDEX.$lang && return + $BSDCFG_LOCAL_LIBE/*/INDEX.$lang && return $SUCCESS # No match, fall-thru to non-i18n sources fi awk -v keyword="$keyword" "$f_index_file_awk" \ diff --git a/usr.sbin/bsdconfig/share/device.subr b/usr.sbin/bsdconfig/share/device.subr index bbd3a104f15..b92080934c1 100644 --- a/usr.sbin/bsdconfig/share/device.subr +++ b/usr.sbin/bsdconfig/share/device.subr @@ -552,7 +552,7 @@ f_device_find() f_device_init() { local name="$1" init_func - device_$name get init init_func || return + device_$name get init init_func || return $? ${init_func:-:} $name } @@ -564,7 +564,7 @@ f_device_init() f_device_get() { local name="$1" file="$2" probe="$3" get_func - device_$name get get get_func || return + device_$name get get get_func || return $? ${get_func:-:} $name "$file" ${3+"$probe"} } @@ -575,7 +575,7 @@ f_device_get() f_device_shutdown() { local name="$1" shutdown_func - device_$name get shutdown shutdown_func || return + device_$name get shutdown shutdown_func || return $? ${shutdown_func:-:} $name } @@ -597,7 +597,7 @@ f_device_menu() [ "$devtype" = "$type" ] || continue devs="$devs $dev" done - [ "$devs" ] || return $FAILURE + [ "$devs" ] || return $DIALOG_CANCEL local desc menu_list= for dev in $devs; do @@ -637,7 +637,7 @@ f_device_menu() ) local retval=$? - [ $retval -ne 2 ] && break + [ $retval -ne $DIALOG_HELP ] && break # Otherwise, the Help button was pressed f_show_help "$helpfile" # ...then loop back to menu @@ -646,7 +646,7 @@ f_device_menu() [ "$errexit" ] && set -e - if [ $retval -eq 0 ]; then + if [ $retval -eq $DIALOG_OK ]; then # Clean up the output of [X]dialog(1) and return it f_dialog_data_sanitize mtag echo "$mtag" >&2 diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index bde3753ba5b..ff7fc863582 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -73,6 +73,18 @@ unset XDIALOG_HIGH_DIALOG_COMPAT unset XDIALOG_FORCE_AUTOSIZE unset XDIALOG_INFOBOX_TIMEOUT +# +# Exit codes for [X]dialog(1) +# +DIALOG_OK=${SUCCESS:-0} +DIALOG_CANCEL=${FAILURE:-1} +DIALOG_HELP=2 +DIALOG_ITEM_HELP=2 +DIALOG_EXTRA=3 +DIALOG_ITEM_HELP=4 +export DIALOG_ERROR=254 # sh(1) can't handle the default of `-1' +DIALOG_ESC=255 + # # Default behavior is to call f_dialog_init() automatically when loaded. # diff --git a/usr.sbin/bsdconfig/share/media/any.subr b/usr.sbin/bsdconfig/share/media/any.subr index 3675713389c..516d2d2bd4b 100644 --- a/usr.sbin/bsdconfig/share/media/any.subr +++ b/usr.sbin/bsdconfig/share/media/any.subr @@ -113,11 +113,10 @@ f_media_get_type() f_dialog_data_sanitize mtag f_dprintf "retval=%s mtag=[%s]" $retval "$mtag" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$MEDIA_HELPFILE" continue - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then return $FAILURE fi diff --git a/usr.sbin/bsdconfig/share/media/cdrom.subr b/usr.sbin/bsdconfig/share/media/cdrom.subr index cd68029e079..c59f88aa585 100644 --- a/usr.sbin/bsdconfig/share/media/cdrom.subr +++ b/usr.sbin/bsdconfig/share/media/cdrom.subr @@ -170,7 +170,7 @@ f_media_shutdown_cdrom() { local dev="$1" err - [ "$CDROM_MOUNTED" ] || return + [ "$CDROM_MOUNTED" ] || return $FAILURE if [ "$CDROM_PREVIOUSLY_MOUNTED" ]; then CDROM_MOUNTED= diff --git a/usr.sbin/bsdconfig/share/media/common.subr b/usr.sbin/bsdconfig/share/media/common.subr index 495aca73ea4..01d50a19e75 100644 --- a/usr.sbin/bsdconfig/share/media/common.subr +++ b/usr.sbin/bsdconfig/share/media/common.subr @@ -127,7 +127,7 @@ f_media_generic_get() fi [ "$probe_type" ] && return $SUCCESS cat "$path" - return + return $? fi done diff --git a/usr.sbin/bsdconfig/share/media/dos.subr b/usr.sbin/bsdconfig/share/media/dos.subr index 440c5c949e3..be4cfc7d9a6 100644 --- a/usr.sbin/bsdconfig/share/media/dos.subr +++ b/usr.sbin/bsdconfig/share/media/dos.subr @@ -148,7 +148,7 @@ f_media_shutdown_dos() { local dev="$1" err - [ "$DOS_MOUNTED" ] || return + [ "$DOS_MOUNTED" ] || return $FAILURE if ! err=$( umount -f "$MOUNTPOINT" 2>&1 ); then err="${err#umount: }"; err="${err#*: }" diff --git a/usr.sbin/bsdconfig/share/media/floppy.subr b/usr.sbin/bsdconfig/share/media/floppy.subr index eb6bc5e5198..62fe64b68d3 100644 --- a/usr.sbin/bsdconfig/share/media/floppy.subr +++ b/usr.sbin/bsdconfig/share/media/floppy.subr @@ -205,7 +205,7 @@ f_media_shutdown_floppy() { local dev="$1" err mp - [ "$FLOPPY_MOUNTED" ] || return + [ "$FLOPPY_MOUNTED" ] || return $FAILURE device_$dev get private mp if ! err=$( umount -f "${mp:=$MOUNTPOINT}" 2>&1 ); then diff --git a/usr.sbin/bsdconfig/share/media/ftp.subr b/usr.sbin/bsdconfig/share/media/ftp.subr index 5aad8ee4b2c..d7f33bbb1c2 100644 --- a/usr.sbin/bsdconfig/share/media/ftp.subr +++ b/usr.sbin/bsdconfig/share/media/ftp.subr @@ -213,7 +213,7 @@ f_dialog_menu_media_ftp() $height $width $rows \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD - ) || return $FAILURE + ) || return $DIALOG_CANCEL f_dialog_data_sanitize mtag case "$mtag" in @@ -224,7 +224,7 @@ f_dialog_menu_media_ftp() setvar $VAR_FTP_PATH "ftp://$value" esac - return $SUCCESS + return $DIALOG_OK } # f_media_set_ftp @@ -740,7 +740,7 @@ f_media_init_ftp() else f_yesno "$msg_cant_find_distribution" \ "$rel" "$ftp_host" - if [ $? -eq $SUCCESS ]; then + if [ $? -eq $DIALOG_OK ]; then unset $VAR_FTP_PATH f_media_set_ftp && continue fi diff --git a/usr.sbin/bsdconfig/share/media/http.subr b/usr.sbin/bsdconfig/share/media/http.subr index 984d7712a14..97c4ed02f2d 100644 --- a/usr.sbin/bsdconfig/share/media/http.subr +++ b/usr.sbin/bsdconfig/share/media/http.subr @@ -101,7 +101,7 @@ f_dialog_menu_media_http() $height $width $rows \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD - ) || return $FAILURE + ) || return $DIALOG_CANCEL f_dialog_data_sanitize mtag case "$mtag" in @@ -112,7 +112,7 @@ f_dialog_menu_media_http() setvar $VAR_HTTP_PATH "http://$value" esac - return $SUCCESS + return $DIALOG_OK } # f_media_set_http diff --git a/usr.sbin/bsdconfig/share/media/nfs.subr b/usr.sbin/bsdconfig/share/media/nfs.subr index 9ce0467df45..8db5a069aa3 100644 --- a/usr.sbin/bsdconfig/share/media/nfs.subr +++ b/usr.sbin/bsdconfig/share/media/nfs.subr @@ -233,7 +233,7 @@ f_media_shutdown_nfs() { local dev="$1" err - [ "$NFS_MOUNTED" ] || return + [ "$NFS_MOUNTED" ] || return $FAILURE f_dprintf "Unmounting NFS partition on %s" "$MOUNTPOINT" if ! err=$( umount -f "$MOUNTPOINT" 2>&1 ); then diff --git a/usr.sbin/bsdconfig/share/media/options.subr b/usr.sbin/bsdconfig/share/media/options.subr index 3f71a96a81f..bc9568e6510 100644 --- a/usr.sbin/bsdconfig/share/media/options.subr +++ b/usr.sbin/bsdconfig/share/media/options.subr @@ -224,11 +224,10 @@ f_media_options_menu() defaultitem="$mtag" f_dprintf "retval=%s mtag=[%s]" $retval "$mtag" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$OPTIONS_HELPFILE" continue - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then break # to success fi diff --git a/usr.sbin/bsdconfig/share/media/tcpip.subr b/usr.sbin/bsdconfig/share/media/tcpip.subr index 6b4791edcaa..5957b90728b 100644 --- a/usr.sbin/bsdconfig/share/media/tcpip.subr +++ b/usr.sbin/bsdconfig/share/media/tcpip.subr @@ -471,10 +471,10 @@ f_dialog_validate_tcpip() ! f_validate_gateway "$gateway" "$ipaddr" "$netmask"; then f_show_msg "$msg_invalid_gateway_ipv4_address_specified" else - return $SUCCESS + return $DIALOG_OK fi - return $FAILURE + return $DIALOG_CANCEL } # f_ifconfig_inet $interface [$var_to_set] @@ -1102,7 +1102,7 @@ f_device_dialog_tcp() local use_dhcp="" use_rtsol="" local _ipaddr _netmask _extras - [ "$dev" ] || return $FAILURE + [ "$dev" ] || return $DIALOG_CANCEL # Initialize vars from previous device values local private @@ -1125,7 +1125,6 @@ f_device_dialog_tcp() unset $VAR_NONINTERACTIVE fi - # # Try a RTSOL scan if such behavior is desired. # If the variable was configured and is YES, do it. @@ -1321,7 +1320,7 @@ f_device_dialog_tcp() if [ ! "$cp" ]; then # User either chose "Cancel", pressed # ESC, or blanked every form field - return $FAILURE + return $DIALOG_CANCEL else n=$( echo "$cp" | f_number_of_lines ) [ $n -eq 1 ] && case "$cp" in HELP*) @@ -1404,14 +1403,12 @@ f_device_dialog_tcp() f_dialog_data_sanitize cp f_dprintf "retval=%u mtag=[%s]" $retval "$cp" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$TCP_HELPFILE" continue - elif [ $retval -ne 0 ]; then - # User chose "Cancel" or pressed ESC + elif [ $retval -ne $DIALOG_OK ]; then f_dialog_title_restore - return $FAILURE + return $DIALOG_CANCEL fi case "$cp" in @@ -1490,7 +1487,7 @@ f_device_dialog_tcp() [ "$use_dhcp" ] || f_config_resolv # XXX this will do it on the MFS copy - return $SUCCESS + return $DIALOG_OK } # f_device_scan_tcp [$var_to_set] @@ -1586,15 +1583,15 @@ f_device_select_tcp() if [ ${cnt:=0} -gt 0 ]; then dev="${devs%%[$IFS]*}" f_device_dialog_tcp $dev - if [ $? -eq $SUCCESS ]; then + if [ $? -eq $DIALOG_OK ]; then setvar $VAR_NETWORK_DEVICE $dev - return $SUCCESS + return $DIALOG_OK fi fi done f_interactive && f_show_msg "$msg_no_network_devices" - return $FAILURE + return $DIALOG_CANCEL fi # $network_dev @@ -1610,18 +1607,18 @@ f_device_select_tcp() if f_dialog_yesno "$msg_assume_network_is_already_configured" then setvar $VAR_NETWORK_DEVICE $dev - return $SUCCESS + return $DIALOG_OK fi fi local retval=$SUCCESS if [ ${cnt:=0} -eq 0 ]; then f_show_msg "$msg_no_network_devices" - retval=$FAILURE + retval=$DIALOG_CANCEL elif [ $cnt -eq 1 ]; then f_device_dialog_tcp $dev retval=$? - [ $retval -eq $SUCCESS ] && setvar $VAR_NETWORK_DEVICE $dev + [ $retval -eq $DIALOG_OK ] && setvar $VAR_NETWORK_DEVICE $dev else local title="$msg_network_interface_information_required" local prompt="$msg_please_select_ethernet_device_to_configure" @@ -1632,15 +1629,15 @@ f_device_select_tcp() "$NETWORK_DEVICE_HELPFILE" \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) retval=$? - [ "$dev" ] || return $FAILURE + [ "$dev" ] || return $DIALOG_CANCEL f_device_find "$dev" $DEVICE_TYPE_NETWORK devs - [ "$devs" ] || return $FAILURE + [ "$devs" ] || return $DIALOG_CANCEL dev="${devs%%[$IFS]*}" f_device_dialog_tcp $dev retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then f_struct_copy device_$dev device_network setvar $VAR_NETWORK_DEVICE network else @@ -1675,7 +1672,7 @@ f_dialog_menu_select_tcp() "$name" fi fi - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/share/media/ufs.subr b/usr.sbin/bsdconfig/share/media/ufs.subr index cc634757e80..2de29b3f454 100644 --- a/usr.sbin/bsdconfig/share/media/ufs.subr +++ b/usr.sbin/bsdconfig/share/media/ufs.subr @@ -177,7 +177,7 @@ f_media_shutdown_ufs() { local dev="$1" err - [ "$UFS_MOUNTED" ] || return + [ "$UFS_MOUNTED" ] || return $FAILURE if ! err=$( umount -f "$MOUNTPOINT" 2>&1 ); then err="${err#umount: }"; err="${err#*: }" diff --git a/usr.sbin/bsdconfig/share/media/usb.subr b/usr.sbin/bsdconfig/share/media/usb.subr index e345ae0a874..f7afc296067 100644 --- a/usr.sbin/bsdconfig/share/media/usb.subr +++ b/usr.sbin/bsdconfig/share/media/usb.subr @@ -158,7 +158,7 @@ f_media_shutdown_usb() { local dev="$1" err - [ "$USB_MOUNTED" ] || return + [ "$USB_MOUNTED" ] || return $FAILURE if ! err=$( umount -f "$MOUNTPOINT" 2>&1 ); then err="${err#umount: }"; err="${err#*: }" diff --git a/usr.sbin/bsdconfig/share/mustberoot.subr b/usr.sbin/bsdconfig/share/mustberoot.subr index 765487dc457..67b4c3ee3b8 100644 --- a/usr.sbin/bsdconfig/share/mustberoot.subr +++ b/usr.sbin/bsdconfig/share/mustberoot.subr @@ -176,9 +176,9 @@ f_become_root_via_sudo() retval=$? # Catch X11-related errors - if [ $retval -eq 255 ]; then + if [ $retval -eq $DIALOG_ESC ]; then f_die $retval "$password" - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then # User cancelled exit $retval fi @@ -316,10 +316,10 @@ f_authenticate_some_user() retval=$? # Catch X11-related errors - [ $retval -eq 255 ] && f_die $retval "$user_pass" + [ $retval -eq $DIALOG_ESC ] && f_die $retval "$user_pass" # Exit if the user cancelled. - [ $retval -eq $SUCCESS ] || exit $retval + [ $retval -eq $DIALOG_OK ] || exit $retval # # Make sure the user exists and is non-root diff --git a/usr.sbin/bsdconfig/share/packages/packages.subr b/usr.sbin/bsdconfig/share/packages/packages.subr index e447b2ed9ae..85aebc80cf1 100755 --- a/usr.sbin/bsdconfig/share/packages/packages.subr +++ b/usr.sbin/bsdconfig/share/packages/packages.subr @@ -132,7 +132,7 @@ f_package_select() package="$1" shift 1 # package for pkgsel in $SELECTED_PACKAGES; do - [ "$package" = "$pkgsel" ] && return + [ "$package" = "$pkgsel" ] && return $SUCCESS done SELECTED_PACKAGES="$SELECTED_PACKAGES $package" f_dprintf "Added %s to selection list" "$package" @@ -312,7 +312,7 @@ f_package_menu_categories() # creates _{varcat}_ninstalled and _{varcat}_nselected local category_list - debug= f_getvar "$var_to_get" category_list || return $FAILURE + debug= f_getvar "$var_to_get" category_list || return $DIALOG_CANCEL # Accent the category menu list with ninstalled/nselected eval f_package_accent_category_menu category_list $category_list @@ -395,7 +395,7 @@ f_package_menu_select() local defaultitem="$3" local hline="$hline_arrows_tab_punc_enter" - f_isinteger "$page" || return $FAILURE + f_isinteger "$page" || return $DIALOG_CANCEL local varcat f_str2varname "$category" varcat @@ -518,7 +518,7 @@ f_package_menu_select() f_dialog_data_sanitize menu_choice f_dialog_menutag_store "$menu_choice" - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then local item item=$( eval f_dialog_menutag2item${SHOW_DESC:+_with_help} \ \"\$menu_choice\" $menu_list ) @@ -603,7 +603,7 @@ f_package_review() done if [ ! "$menu_list" ]; then f_show_msg "$msg_no_packages_were_selected_for_extraction" - return $FAILURE # They might have selected this by accident + return $DIALOG_CANCEL # Might have selected this by accident fi menu_list=$( echo "$menu_list" | sort ) @@ -660,7 +660,7 @@ f_package_review() f_package_deselect "$package" done - return $SUCCESS + return $DIALOG_OK } # f_package_config @@ -693,7 +693,7 @@ f_package_config() f_dprintf "retval=%u mtag=[%s]" $retval "$category" category_defaultitem="$category" - [ $retval -eq $SUCCESS ] || break + [ $retval -eq $DIALOG_OK ] || break # Maybe the user chose an action (like `Review') case "$category" in @@ -729,14 +729,14 @@ f_package_config() # the Cancel button because stdout will be NULL. # Alternatively, Xdialog(1) will terminate with 1 # if/when Cancel is chosen on any widget. - if [ $retval -eq 255 -o ! "$menu_choice" ]; then - # User pressed ESC or chose Cancel + if [ $retval -eq $DIALOG_ESC -o ! "$menu_choice" ] + then break - elif [ $retval -eq 1 ]; then + elif [ $retval -eq $DIALOG_CANCEL ]; then # Using X11, Xdialog(1) returned 1 for Cancel f_show_msg "%s" "$menu_choice" break - elif [ $retval -ne $SUCCESS ]; then + elif [ $retval -ne $DIALOG_OK ]; then # X11-related error occurred using Xdialog(1) f_show_msg "%s" "$menu_choice" break diff --git a/usr.sbin/bsdconfig/startup/misc b/usr.sbin/bsdconfig/startup/misc index edfc1acbee9..738f73089cf 100755 --- a/usr.sbin/bsdconfig/startup/misc +++ b/usr.sbin/bsdconfig/startup/misc @@ -309,10 +309,10 @@ dialog_input_value() f_dialog_title_restore # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval value="$_input" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN @@ -429,7 +429,7 @@ while :; do ;; esac - [ $? -eq $SUCCESS ] || f_dialog_msgbox "$err\n" + [ $? -eq $DIALOG_OK ] || f_dialog_msgbox "$err\n" done exit $SUCCESS diff --git a/usr.sbin/bsdconfig/startup/rcconf b/usr.sbin/bsdconfig/startup/rcconf index d064224d354..6154d275796 100755 --- a/usr.sbin/bsdconfig/startup/rcconf +++ b/usr.sbin/bsdconfig/startup/rcconf @@ -195,7 +195,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } @@ -236,13 +236,13 @@ while :; do f_dialog_input_view_details continue esac - elif [ $retval -eq 2 ]; then + elif [ $retval -eq $DIALOG_HELP ]; then # The ``Help'' button (labeled "Details") was pressed f_dialog_input_view_details continue fi - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in "X $msg_exit") break ;; diff --git a/usr.sbin/bsdconfig/startup/rcdelete b/usr.sbin/bsdconfig/startup/rcdelete index b6eeba4814a..5769be9b3aa 100755 --- a/usr.sbin/bsdconfig/startup/rcdelete +++ b/usr.sbin/bsdconfig/startup/rcdelete @@ -232,7 +232,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } @@ -249,7 +249,7 @@ dialog_menu_confirm_delete() local menu_list # Calculated below local hline="$hline_arrows_tab_enter" - [ $# -ge 1 ] || return $FAILURE + [ $# -ge 1 ] || return $DIALOG_CANCEL # If asked to delete only one variable, simply ask and return if [ $# -eq 1 ]; then @@ -351,13 +351,13 @@ while :; do f_dialog_input_view_details && dialog_create_main continue esac - elif [ $retval -eq 2 ]; then + elif [ $retval -eq $DIALOG_HELP ]; then # The ``Help'' button (labeled "Details") was pressed f_dialog_input_view_details && dialog_create_main continue fi - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in "X $msg_exit_cancel") break ;; diff --git a/usr.sbin/bsdconfig/startup/rcvar b/usr.sbin/bsdconfig/startup/rcvar index 7d29bfcd5d2..e206ed47790 100755 --- a/usr.sbin/bsdconfig/startup/rcvar +++ b/usr.sbin/bsdconfig/startup/rcvar @@ -158,7 +158,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" f_dialog_default_store "$menu_choice" - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then local item item=$( eval f_dialog_menutag2item${SHOW_DESC:+_with_help} \ \"\$menu_choice\" $menu_list ) diff --git a/usr.sbin/bsdconfig/startup/share/rcconf.subr b/usr.sbin/bsdconfig/startup/share/rcconf.subr index 4c3e0d67600..f8f9a66c426 100644 --- a/usr.sbin/bsdconfig/startup/share/rcconf.subr +++ b/usr.sbin/bsdconfig/startup/share/rcconf.subr @@ -336,7 +336,7 @@ f_dialog_input_view_details() f_dialog_title_restore - [ $retval -eq 0 ] || return $FAILURE + [ $retval -eq $DIALOG_OK ] || return $DIALOG_CANCEL case "$mtag" in "R $msg_reset") @@ -465,7 +465,7 @@ f_dialog_input_rcvar() # Return if user either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_please_enter_rcvar_name" \ - "$_input" "$hline_alnum_tab_enter" || return + "$_input" "$hline_alnum_tab_enter" || return $? # Check for invalid entry (1of2) if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then @@ -486,7 +486,7 @@ f_dialog_input_rcvar() f_dprintf "f_dialog_input_rcvar: rcvar->[%s]" "$rcvar" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/startup/share/rcedit.subr b/usr.sbin/bsdconfig/startup/share/rcedit.subr index 64ee1ccf0d8..fe0989def06 100644 --- a/usr.sbin/bsdconfig/startup/share/rcedit.subr +++ b/usr.sbin/bsdconfig/startup/share/rcedit.subr @@ -69,11 +69,11 @@ f_dialog_rcedit() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg" "$_input" \ - "$hline_alnum_punc_tab_enter" || return + "$hline_alnum_punc_tab_enter" || return $? # Return if the value has not changed from current local cur_val="$( f_sysrc_get "$var" )" - [ "$_input" = "$cur_val" ] && return $SUCCESS + [ "$_input" = "$cur_val" ] && return $DIALOG_OK f_dprintf "%s: [%s]->[%s]" "$var" "$cur_val" "$_input" diff --git a/usr.sbin/bsdconfig/timezone/timezone b/usr.sbin/bsdconfig/timezone/timezone index 709c8a81a36..d4b7ab5d065 100755 --- a/usr.sbin/bsdconfig/timezone/timezone +++ b/usr.sbin/bsdconfig/timezone/timezone @@ -226,7 +226,7 @@ if [ "$_PATH_WALL_CMOS_CLOCK" -a ! "$SKIPUTC" ]; then result=$? fi - if [ $result -eq 0 ]; then + if [ $result -eq $DIALOG_OK ]; then # User chose YES [ "$REALLYDOIT" ] && f_quietly rm -f "$_PATH_WALL_CMOS_CLOCK" @@ -250,7 +250,7 @@ if [ $# -ge 1 ]; then result=$? f_dialog_title_restore - if [ $result -eq 0 ]; then + if [ $result -eq $DIALOG_OK ]; then # User chose YES f_install_zoneinfo_file "$default" result=$? @@ -287,7 +287,7 @@ while :; do retval=$? f_dialog_menutag_fetch mtag - if [ $retval -ne 0 ]; then + if [ $retval -ne $DIALOG_OK ]; then [ "$TZ_OR_FAIL" ] && f_die exit $SUCCESS fi @@ -368,7 +368,7 @@ while :; do f_dialog_data_sanitize tag defaultctry="$tag" - if [ $retval -ne 0 ]; then + if [ $retval -ne $DIALOG_OK ]; then NEED_CONTINENT=1 continue # back to main menu fi @@ -427,7 +427,7 @@ while :; do f_dialog_data_sanitize n defaultzone="$n" - if [ $retval -ne 0 ]; then + if [ $retval -ne $DIALOG_OK ]; then [ $nitems -eq 1 ] && NEED_CONTINENT=1 NEED_COUNTRY=1 continue @@ -441,7 +441,7 @@ while :; do f_confirm_zone "$real_continent/$filename" || continue fi - [ $retval -eq 0 ] || continue # back to main menu + [ $retval -eq $DIALOG_OK ] || continue # back to main menu if ! f_install_zoneinfo "$real_continent/$filename"; then [ $nzones -lt 0 ] && NEED_COUNTRY=1 diff --git a/usr.sbin/bsdconfig/usermgmt/groupdel b/usr.sbin/bsdconfig/usermgmt/groupdel index 30762168eed..2f4116409bb 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupdel +++ b/usr.sbin/bsdconfig/usermgmt/groupdel @@ -75,7 +75,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" defaultitem="$mtag" - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die [ "$mtag" = "X $msg_exit" ] && break diff --git a/usr.sbin/bsdconfig/usermgmt/groupedit b/usr.sbin/bsdconfig/usermgmt/groupedit index 1f1c744d62c..3eacaffc880 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupedit +++ b/usr.sbin/bsdconfig/usermgmt/groupedit @@ -75,7 +75,7 @@ while :; do f_dprintf "retval=%s mtag=[%s]" $retval "$mtag" defaultitem="$mtag" - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die [ "$mtag" = "X $msg_exit" ] && break diff --git a/usr.sbin/bsdconfig/usermgmt/groupinput b/usr.sbin/bsdconfig/usermgmt/groupinput index 6fff40fa372..5b60bb24c50 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupinput +++ b/usr.sbin/bsdconfig/usermgmt/groupinput @@ -162,9 +162,9 @@ if [ "$mode" = "Add" ]; then f_dialog_noyes "$msg_use_default_values_for_all_account_details" retval=$? - if [ $retval -eq 255 ]; then # User pressed ESC + if [ $retval -eq $DIALOG_ESC ]; then exit $SUCCESS - elif [ $retval -ne $SUCCESS ]; then + elif [ $retval -ne $DIALOG_OK ]; then # # Ask a series of questions to pre-fill the editor screen. # @@ -174,9 +174,9 @@ if [ "$mode" = "Add" ]; then # [ "$passwdtype" = "yes" ] && - { f_dialog_input_group_password || exit 0; } - f_dialog_input_group_gid || exit 0 - f_dialog_input_group_members || exit 0 + { f_dialog_input_group_password || exit $SUCCESS; } + f_dialog_input_group_gid || exit $SUCCESS + f_dialog_input_group_members || exit $SUCCESS fi fi @@ -255,13 +255,11 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" # Exit if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in X) # Exit - if [ "$save_flag" ]; then - save_changes || continue - fi + [ "$save_flag" ] && { save_changes || continue; } break ;; 1) # Group Name @@ -274,7 +272,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" # Loop if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue [ "$mtag" = "X $msg_exit" ] && continue diff --git a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr index 93d981acd98..2ca141eef29 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr @@ -125,15 +125,15 @@ f_dialog_input_group_name() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_group" "$_input" \ - "$hline_alnum_tab_enter" || return + "$hline_alnum_tab_enter" || return $? # Check for no-change - [ "$_input" = "$_name" ] && return $SUCCESS + [ "$_input" = "$_name" ] && return $DIALOG_OK # Check for reversion if [ "$_input" = "$cur_group_name" ]; then group_name="$cur_group_name" - return $SUCCESS + return $DIALOG_OK fi # Check for NULL entry @@ -161,7 +161,7 @@ f_dialog_input_group_name() f_dprintf "group_name: [%s]->[%s]" "$cur_group_name" "$group_name" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_group_password @@ -210,7 +210,7 @@ f_dialog_input_group_password() debug= f_dialog_line_sanitize _password1 # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval _password2=$( $DIALOG \ --title "$DIALOG_TITLE" \ @@ -227,7 +227,7 @@ f_dialog_input_group_password() debug= f_dialog_line_sanitize _password2 # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval # Check for password mismatch if [ "$_password1" != "$_password2" ]; then @@ -239,9 +239,9 @@ f_dialog_input_group_password() if [ ! "$_password1" ]; then f_dialog_yesno "$msg_disable_password_auth_for_group" local retval=$? - if [ $retval -eq 255 ]; then # ESC was pressed + if [ $retval -eq $DIALOG_ESC ]; then return $retval - elif [ $retval -eq $SUCCESS ]; then + elif [ $retval -eq $DIALOG_OK ]; then pw_group_password_disable=1 else continue # back to password prompt @@ -258,7 +258,7 @@ f_dialog_input_group_password() f_dprintf "group_password: [%s]->[%s]" \ "$cur_group_password" "$group_password" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_group_gid [$group_gid] @@ -273,14 +273,14 @@ f_dialog_input_group_gid() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_group_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return + "$_input" "$hline_num_tab_enter" || return $? group_gid="$_input" save_flag=1 f_dprintf "group_gid: [%s]->[%s]" "$cur_group_gid" "$group_gid" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_group_members [$group_members] @@ -329,7 +329,7 @@ f_dialog_input_group_members() f_dprintf "retval=%u menu_choice=[%s]" $retval "$menu_choice" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval local _group_members case "$menu_choice" in @@ -399,7 +399,7 @@ f_dialog_input_group_members() f_dprintf "group_members: [%s]->[%s]" \ "$cur_group_members" "$group_members" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr index 21bfbd37589..bd2fc9b6fca 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr @@ -206,7 +206,7 @@ f_dialog_input_member_groups() f_dprintf "pw_member_groups: [%s]->[%s]" \ "$cur_pw_member_groups" "$pw_member_groups" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_name [$name] @@ -229,15 +229,15 @@ f_dialog_input_name() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_login" "$_input" \ - "$hline_alnum_tab_enter" || return + "$hline_alnum_tab_enter" || return $? # Check for no-change - [ "$_input" = "$_name" ] && return $SUCCESS + [ "$_input" = "$_name" ] && return $DIALOG_OK # Check for reversion if [ "$_input" = "$cur_pw_name" ]; then pw_name="$cur_pw_name" - return $SUCCESS + return $DIALOG_OK fi # Check for NULL entry @@ -265,7 +265,7 @@ f_dialog_input_name() f_dprintf "pw_name: [%s]->[%s]" "$cur_pw_name" "$pw_name" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_password @@ -336,9 +336,9 @@ f_dialog_input_password() if [ ! "$_password1" ]; then f_dialog_yesno "$msg_disable_password_auth_for_account" local retval=$? - if [ $retval -eq 255 ]; then # ESC was pressed + if [ $retval -eq $DIALOG_ESC ]; then return $retval - elif [ $retval -eq $SUCCESS ]; then + elif [ $retval -eq $DIALOG_OK ]; then pw_password_disable=1 else continue # back to password prompt @@ -354,7 +354,7 @@ f_dialog_input_password() f_dprintf "pw_password: [%s]->[%s]" "$cur_pw_password" "$pw_password" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_gecos [$gecos] @@ -370,14 +370,14 @@ f_dialog_input_gecos() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_full_name" "$_input" \ - "$hline_alnum_punc_tab_enter" || return + "$hline_alnum_punc_tab_enter" || return $? pw_gecos="$_input" save_flag=1 f_dprintf "pw_gecos: [%s]->[%s]" "$cur_pw_gecos" "$pw_gecos" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_uid [$uid] @@ -392,14 +392,14 @@ f_dialog_input_uid() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_user_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return + "$_input" "$hline_num_tab_enter" || return $? pw_uid="$_input" save_flag=1 f_dprintf "pw_uid: [%s]->[%s]" "$cur_pw_uid" "$pw_uid" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_gid [$gid] @@ -414,14 +414,14 @@ f_dialog_input_gid() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_group_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return + "$_input" "$hline_num_tab_enter" || return $? pw_gid="$_input" save_flag=1 f_dprintf "pw_gid: [%s]->[%s]" "$cur_pw_gid" "$pw_gid" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_class [$class] @@ -436,14 +436,14 @@ f_dialog_input_class() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_login_class" "$_input" \ - "$hline_alnum_tab_enter" || return + "$hline_alnum_tab_enter" || return $? pw_class="$_input" save_flag=1 f_dprintf "pw_class: [%s]->[%s]" "$cur_pw_class" "$pw_class" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_expire_password [$seconds] @@ -508,7 +508,7 @@ f_dialog_input_expire_password() f_dprintf "retval=%u date_type=[%s]" $retval "$date_type" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval case "$date_type" in 1) # Password does not expire @@ -538,7 +538,7 @@ f_dialog_input_expire_password() f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date" # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue _input_time= [ "$secs" ] && _input_time=$( date -j \ @@ -559,7 +559,7 @@ f_dialog_input_expire_password() f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time" # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue _input=$( date \ -j -f "%d/%m/%Y %T" \ @@ -627,7 +627,7 @@ f_dialog_input_expire_password() f_dprintf "pw_password_expire: [%s]->[%s]" \ "$cur_pw_password_expire" "$pw_password_expire" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_expire_account [$seconds] @@ -692,7 +692,7 @@ f_dialog_input_expire_account() f_dprintf "retval=%u date_type=[%s]" $retval "$date_type" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval case "$date_type" in 1) # Account does not expire @@ -722,7 +722,7 @@ f_dialog_input_expire_account() f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date" # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue _input_time= [ "$secs" ] && _input_time=$( date -j \ @@ -743,7 +743,7 @@ f_dialog_input_expire_account() f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time" # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue _input=$( date \ -j -f "%d/%m/%Y %T" \ @@ -811,7 +811,7 @@ f_dialog_input_expire_account() f_dprintf "pw_account_expire: [%s]->[%s]" \ "$cur_pw_account_expire" "$pw_account_expire" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_home_dir [$home_dir] @@ -826,14 +826,14 @@ f_dialog_input_home_dir() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_home_directory" "$_input" \ - "$hline_alnum_punc_tab_enter" || return + "$hline_alnum_punc_tab_enter" || return $? pw_home_dir="$_input" save_flag=1 f_dprintf "pw_home_dir: [%s]->[%s]" "$cur_pw_home_dir" "$pw_home_dir" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_home_create @@ -850,7 +850,7 @@ f_dialog_input_home_create() f_dialog_yesno "$msg_create_home_directory" retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then pw_home_create="$msg_yes" else pw_home_create="$msg_no" @@ -860,7 +860,7 @@ f_dialog_input_home_create() f_dprintf "pw_home_create: [%s]->[%s]" \ "$cur_pw_home_create" "$pw_home_create" - [ $retval -ne 255 ] # return failure if user pressed ESC + [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } # f_dialog_input_group_delete @@ -893,7 +893,7 @@ f_dialog_input_group_delete() fi retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then pw_group_delete="$msg_yes" else pw_group_delete="$msg_no" @@ -903,7 +903,7 @@ f_dialog_input_group_delete() f_dprintf "pw_group_delete: [%s]->[%s]" \ "$cur_pw_group_delete" "$pw_group_delete" - [ $retval -ne 255 ] # return failure if user pressed ESC + [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } # f_dialog_input_home_delete @@ -920,7 +920,7 @@ f_dialog_input_home_delete() f_dialog_yesno "$msg_delete_home_directory" retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then pw_home_delete="$msg_yes" else pw_home_delete="$msg_no" @@ -930,7 +930,7 @@ f_dialog_input_home_delete() f_dprintf "pw_home_delete: [%s]->[%s]" \ "$cur_pw_home_delete" "$pw_home_delete" - [ $retval -ne 255 ] # return failure if user pressed ESC + [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } # f_dialog_input_dotfiles_create @@ -948,7 +948,7 @@ f_dialog_input_dotfiles_create() f_dialog_yesno "$msg_create_dotfiles" retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then pw_dotfiles_create="$msg_yes" else pw_dotfiles_create="$msg_no" @@ -958,7 +958,7 @@ f_dialog_input_dotfiles_create() f_dprintf "pw_dotfiles_create: [%s]->[%s]" \ "$cur_pw_dotfiles_create" "$pw_dotfiles_create" - [ $retval -ne 255 ] # return failure if user pressed ESC + [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } # f_dialog_input_shell [$shell] @@ -1015,7 +1015,7 @@ f_dialog_input_shell() f_dprintf "pw_shell: [%s]->[%s]" "$cur_pw_shell" "$pw_shell" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/usermgmt/userdel b/usr.sbin/bsdconfig/usermgmt/userdel index f2664979885..299e25bc10e 100755 --- a/usr.sbin/bsdconfig/usermgmt/userdel +++ b/usr.sbin/bsdconfig/usermgmt/userdel @@ -75,7 +75,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" defaultitem="$mtag" - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die [ "$mtag" = "X $msg_exit" ] && break diff --git a/usr.sbin/bsdconfig/usermgmt/useredit b/usr.sbin/bsdconfig/usermgmt/useredit index 64b7b8e2fc8..a4bee68b70b 100755 --- a/usr.sbin/bsdconfig/usermgmt/useredit +++ b/usr.sbin/bsdconfig/usermgmt/useredit @@ -75,7 +75,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" defaultitem="$mtag" - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die [ "$mtag" = "X $msg_exit" ] && break diff --git a/usr.sbin/bsdconfig/usermgmt/userinput b/usr.sbin/bsdconfig/usermgmt/userinput index 196262b229e..aad6ef3f394 100755 --- a/usr.sbin/bsdconfig/usermgmt/userinput +++ b/usr.sbin/bsdconfig/usermgmt/userinput @@ -257,9 +257,9 @@ if [ "$mode" = "Add" ]; then f_dialog_noyes "$msg_use_default_values_for_all_account_details" retval=$? - if [ $retval -eq 255 ]; then # User pressed ESC + if [ $retval -eq $DIALOG_ESC ]; then exit $SUCCESS - elif [ $retval -ne $SUCCESS ]; then + elif [ $retval -ne $DIALOG_OK ]; then # # Ask a series of questions to pre-fill the editor screen. # @@ -431,7 +431,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" # Exit if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in X) # Exit @@ -450,7 +450,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" # Loop if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue [ "$mtag" = "X $msg_exit" ] && continue diff --git a/usr.sbin/bsdconfig/usermgmt/usermgmt b/usr.sbin/bsdconfig/usermgmt/usermgmt index 0a4c160b35b..5335bd6fe4c 100755 --- a/usr.sbin/bsdconfig/usermgmt/usermgmt +++ b/usr.sbin/bsdconfig/usermgmt/usermgmt @@ -100,7 +100,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } @@ -136,11 +136,10 @@ while :; do f_dialog_menutag_fetch mtag f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$USERMGMT_HELPFILE" continue - elif [ $retval -ne $SUCCESS ]; then + elif [ $retval -ne $DIALOG_OK ]; then f_die fi From 1d2df300e9b591f03281c085bf18fcac9c67b4c9 Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Wed, 9 Oct 2013 11:57:53 +0000 Subject: [PATCH 02/18] - Substitute sbdrop_internal() with sbcut_internal(). The latter doesn't free mbufs, but return chain of free mbufs to a caller. Caller can either reuse them or return to allocator in a batch manner. - Implement sbdrop()/sbdrop_locked() as a wrapper around sbcut_internal(). - Expose sbcut_locked() for outside usage. Sponsored by: Netflix Sponsored by: Nginx, Inc. Approved by: re (marius) --- sys/kern/uipc_sockbuf.c | 45 +++++++++++++++++++++++++++++++---------- sys/sys/sockbuf.h | 2 ++ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c index 9fa8ae0aa7b..c23995f72c6 100644 --- a/sys/kern/uipc_sockbuf.c +++ b/sys/kern/uipc_sockbuf.c @@ -65,7 +65,7 @@ u_long sb_max_adj = static u_long sb_efficiency = 8; /* parameter for sbreserve() */ -static void sbdrop_internal(struct sockbuf *sb, int len); +static struct mbuf *sbcut_internal(struct sockbuf *sb, int len); static void sbflush_internal(struct sockbuf *sb); /* @@ -818,7 +818,7 @@ sbflush_internal(struct sockbuf *sb) */ if (!sb->sb_cc && (sb->sb_mb == NULL || sb->sb_mb->m_len)) break; - sbdrop_internal(sb, (int)sb->sb_cc); + m_freem(sbcut_internal(sb, (int)sb->sb_cc)); } if (sb->sb_cc || sb->sb_mb || sb->sb_mbcnt) panic("sbflush_internal: cc %u || mb %p || mbcnt %u", @@ -843,15 +843,16 @@ sbflush(struct sockbuf *sb) } /* - * Drop data from (the front of) a sockbuf. + * Cut data from (the front of) a sockbuf. */ -static void -sbdrop_internal(struct sockbuf *sb, int len) +static struct mbuf * +sbcut_internal(struct sockbuf *sb, int len) { - struct mbuf *m; - struct mbuf *next; + struct mbuf *m, *n, *next, *mfree; next = (m = sb->sb_mb) ? m->m_nextpkt : 0; + mfree = NULL; + while (len > 0) { if (m == 0) { if (next == 0) @@ -872,11 +873,17 @@ sbdrop_internal(struct sockbuf *sb, int len) } len -= m->m_len; sbfree(sb, m); - m = m_free(m); + n = m->m_next; + m->m_next = mfree; + mfree = m; + m = n; } while (m && m->m_len == 0) { sbfree(sb, m); - m = m_free(m); + n = m->m_next; + m->m_next = mfree; + mfree = m; + m = n; } if (m) { sb->sb_mb = m; @@ -894,6 +901,8 @@ sbdrop_internal(struct sockbuf *sb, int len) } else if (m->m_nextpkt == NULL) { sb->sb_lastrecord = m; } + + return (mfree); } /* @@ -904,17 +913,31 @@ sbdrop_locked(struct sockbuf *sb, int len) { SOCKBUF_LOCK_ASSERT(sb); + m_freem(sbcut_internal(sb, len)); +} - sbdrop_internal(sb, len); +/* + * Drop data from (the front of) a sockbuf, + * and return it to caller. + */ +struct mbuf * +sbcut_locked(struct sockbuf *sb, int len) +{ + + SOCKBUF_LOCK_ASSERT(sb); + return (sbcut_internal(sb, len)); } void sbdrop(struct sockbuf *sb, int len) { + struct mbuf *mfree; SOCKBUF_LOCK(sb); - sbdrop_locked(sb, len); + mfree = sbcut_internal(sb, len); SOCKBUF_UNLOCK(sb); + + m_freem(mfree); } /* diff --git a/sys/sys/sockbuf.h b/sys/sys/sockbuf.h index 402a8f0749f..e7162418e67 100644 --- a/sys/sys/sockbuf.h +++ b/sys/sys/sockbuf.h @@ -140,6 +140,8 @@ struct mbuf * void sbdestroy(struct sockbuf *sb, struct socket *so); void sbdrop(struct sockbuf *sb, int len); void sbdrop_locked(struct sockbuf *sb, int len); +struct mbuf * + sbcut_locked(struct sockbuf *sb, int len); void sbdroprecord(struct sockbuf *sb); void sbdroprecord_locked(struct sockbuf *sb); void sbflush(struct sockbuf *sb); From c11a15bf8d77625b462f4007508b8dee6cee22cb Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Wed, 9 Oct 2013 12:00:38 +0000 Subject: [PATCH 03/18] When processing ACK in tcp_do_segment, use sbcut_locked() instead of sbdrop_locked() to cut acked mbufs from the socket buffer. Free this chain a batch manner after the socket buffer lock is dropped. This measurably reduces contention on socket buffer. Sponsored by: Netflix Sponsored by: Nginx, Inc. Approved by: re (marius) --- sys/netinet/tcp_input.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 0d7eb193349..9a2ce9eaf77 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1461,6 +1461,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, u_long tiwin; char *s; struct in_conninfo *inc; + struct mbuf *mfree; struct tcpopt to; #ifdef TCPDEBUG @@ -2718,15 +2719,17 @@ process_ACK: SOCKBUF_LOCK(&so->so_snd); if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; - sbdrop_locked(&so->so_snd, (int)so->so_snd.sb_cc); + mfree = sbcut_locked(&so->so_snd, + (int)so->so_snd.sb_cc); ourfinisacked = 1; } else { - sbdrop_locked(&so->so_snd, acked); + mfree = sbcut_locked(&so->so_snd, acked); tp->snd_wnd -= acked; ourfinisacked = 0; } /* NB: sowwakeup_locked() does an implicit unlock. */ sowwakeup_locked(so); + m_freem(mfree); /* Detect una wraparound. */ if (!IN_RECOVERY(tp->t_flags) && SEQ_GT(tp->snd_una, tp->snd_recover) && From 1008ac5eb7b01f2c2dd02c21a49c587e3615428d Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 12:03:04 +0000 Subject: [PATCH 04/18] Fix NOP-In/NOP-Out payload handling. Previous way didn't work at all; fortunately nothing seems to actually use this feature, but it's required by standard. Approved by: re (glebius) Sponsored by: FreeBSD Foundation --- sys/cam/ctl/ctl_frontend_iscsi.c | 41 +++++++++++++++++++++++++------ sys/dev/iscsi/iscsi.c | 42 +++++++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/sys/cam/ctl/ctl_frontend_iscsi.c b/sys/cam/ctl/ctl_frontend_iscsi.c index e8e2d33a42d..1f310d926f8 100644 --- a/sys/cam/ctl/ctl_frontend_iscsi.c +++ b/sys/cam/ctl/ctl_frontend_iscsi.c @@ -455,6 +455,9 @@ cfiscsi_pdu_handle_nop_out(struct icl_pdu *request) struct iscsi_bhs_nop_out *bhsno; struct iscsi_bhs_nop_in *bhsni; struct icl_pdu *response; + void *data = NULL; + size_t datasize; + int error; cs = PDU_SESSION(request); bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; @@ -468,9 +471,26 @@ cfiscsi_pdu_handle_nop_out(struct icl_pdu *request) return; } + datasize = icl_pdu_data_segment_length(request); + if (datasize > 0) { + data = malloc(datasize, M_CFISCSI, M_NOWAIT | M_ZERO); + if (data == NULL) { + CFISCSI_SESSION_WARN(cs, "failed to allocate memory; " + "dropping connection"); + icl_pdu_free(request); + cfiscsi_session_terminate(cs); + return; + } + icl_pdu_get_data(request, 0, data, datasize); + } + response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { + CFISCSI_SESSION_WARN(cs, "failed to allocate memory; " + "droppping connection"); + free(data, M_CFISCSI); icl_pdu_free(request); + cfiscsi_session_terminate(cs); return; } bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs; @@ -478,14 +498,19 @@ cfiscsi_pdu_handle_nop_out(struct icl_pdu *request) bhsni->bhsni_flags = 0x80; bhsni->bhsni_initiator_task_tag = bhsno->bhsno_initiator_task_tag; bhsni->bhsni_target_transfer_tag = 0xffffffff; - -#if 0 - /* XXX */ - response->ip_data_len = request->ip_data_len; - response->ip_data_mbuf = request->ip_data_mbuf; - request->ip_data_len = 0; - request->ip_data_mbuf = NULL; -#endif + if (datasize > 0) { + error = icl_pdu_append_data(response, data, datasize, M_NOWAIT); + if (error != 0) { + CFISCSI_SESSION_WARN(cs, "failed to allocate memory; " + "dropping connection"); + free(data, M_CFISCSI); + icl_pdu_free(request); + icl_pdu_free(response); + cfiscsi_session_terminate(cs); + return; + } + free(data, M_CFISCSI); + } icl_pdu_free(request); cfiscsi_pdu_queue(response); diff --git a/sys/dev/iscsi/iscsi.c b/sys/dev/iscsi/iscsi.c index 8bd22079cc6..2d347f3d884 100644 --- a/sys/dev/iscsi/iscsi.c +++ b/sys/dev/iscsi/iscsi.c @@ -726,10 +726,15 @@ iscsi_error_callback(struct icl_conn *ic) static void iscsi_pdu_handle_nop_in(struct icl_pdu *response) { + struct iscsi_session *is; struct iscsi_bhs_nop_out *bhsno; struct iscsi_bhs_nop_in *bhsni; struct icl_pdu *request; + void *data = NULL; + size_t datasize; + int error; + is = PDU_SESSION(response); bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs; if (bhsni->bhsni_target_transfer_tag == 0xffffffff) { @@ -741,22 +746,47 @@ iscsi_pdu_handle_nop_in(struct icl_pdu *response) return; } + datasize = icl_pdu_data_segment_length(response); + if (datasize > 0) { + data = malloc(datasize, M_ISCSI, M_NOWAIT | M_ZERO); + if (data == NULL) { + ISCSI_SESSION_WARN(is, "failed to allocate memory; " + "reconnecting"); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + icl_pdu_get_data(response, 0, data, datasize); + } + request = icl_pdu_new_bhs(response->ip_conn, M_NOWAIT); if (request == NULL) { + ISCSI_SESSION_WARN(is, "failed to allocate memory; " + "reconnecting"); + free(data, M_ISCSI); icl_pdu_free(response); + iscsi_session_reconnect(is); return; } bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | ISCSI_BHS_OPCODE_IMMEDIATE; bhsno->bhsno_flags = 0x80; - bhsno->bhsno_initiator_task_tag = 0xffffffff; /* XXX */ + bhsno->bhsno_initiator_task_tag = 0xffffffff; bhsno->bhsno_target_transfer_tag = bhsni->bhsni_target_transfer_tag; - - request->ip_data_len = response->ip_data_len; - request->ip_data_mbuf = response->ip_data_mbuf; - response->ip_data_len = 0; - response->ip_data_mbuf = NULL; + if (datasize > 0) { + error = icl_pdu_append_data(request, data, datasize, M_NOWAIT); + if (error != 0) { + ISCSI_SESSION_WARN(is, "failed to allocate memory; " + "reconnecting"); + free(data, M_ISCSI); + icl_pdu_free(request); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + free(data, M_ISCSI); + } icl_pdu_free(response); iscsi_pdu_queue_locked(request); From 6dfc67e379b70d5b9d2201f05dc33dd3f9bb1736 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Wed, 9 Oct 2013 12:09:01 +0000 Subject: [PATCH 05/18] Close the race on path ID allocation in xpt_bus_register() if two buses are registered simultaneously. Due to topology unlock between the ID allocation and the bus registration there is a chance that two buses may get the same IDs. That is supposed reason of lock assertion panic in CAM during initial bus scanning after new iscsid initiates two sessions same time. Reported by: trasz Approved by: re (glebus, marius) MFC after: 2 weeks --- sys/cam/cam_xpt.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c index 142195be574..01025ff6f61 100644 --- a/sys/cam/cam_xpt.c +++ b/sys/cam/cam_xpt.c @@ -2415,7 +2415,7 @@ xptsetasyncbusfunc(struct cam_eb *bus, void *arg) struct ccb_setasync *csa = (struct ccb_setasync *)arg; xpt_compile_path(&path, /*periph*/NULL, - bus->sim->path_id, + bus->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); xpt_setup_ccb(&cpi.ccb_h, &path, CAM_PRIORITY_NORMAL); @@ -3840,13 +3840,8 @@ xpt_bus_register(struct cam_sim *sim, device_t parent, u_int32_t bus) /* Couldn't satisfy request */ return (CAM_RESRC_UNAVAIL); } - if (strcmp(sim->sim_name, "xpt") != 0) { - sim->path_id = - xptpathid(sim->sim_name, sim->unit_number, sim->bus_id); - } TAILQ_INIT(&new_bus->et_entries); - new_bus->path_id = sim->path_id; cam_sim_hold(sim); new_bus->sim = sim; timevalclear(&new_bus->last_reset); @@ -3855,6 +3850,8 @@ xpt_bus_register(struct cam_sim *sim, device_t parent, u_int32_t bus) new_bus->generation = 0; xpt_lock_buses(); + sim->path_id = new_bus->path_id = + xptpathid(sim->sim_name, sim->unit_number, sim->bus_id); old_bus = TAILQ_FIRST(&xsoftc.xpt_busses); while (old_bus != NULL && old_bus->path_id < new_bus->path_id) @@ -3958,8 +3955,8 @@ xptnextfreepathid(void) path_id_t pathid; const char *strval; + mtx_assert(&xsoftc.xpt_topo_lock, MA_OWNED); pathid = 0; - xpt_lock_buses(); bus = TAILQ_FIRST(&xsoftc.xpt_busses); retry: /* Find an unoccupied pathid */ @@ -3968,7 +3965,6 @@ retry: pathid++; bus = TAILQ_NEXT(bus, links); } - xpt_unlock_buses(); /* * Ensure that this pathid is not reserved for @@ -3977,7 +3973,6 @@ retry: if (resource_string_value("scbus", pathid, "at", &strval) == 0) { ++pathid; /* Start the search over */ - xpt_lock_buses(); goto retry; } return (pathid); @@ -3993,6 +3988,8 @@ xptpathid(const char *sim_name, int sim_unit, int sim_bus) pathid = CAM_XPT_PATH_ID; snprintf(buf, sizeof(buf), "%s%d", sim_name, sim_unit); + if (strcmp(buf, "xpt0") == 0 && sim_bus == 0) + return (pathid); i = 0; while ((resource_find_match(&i, &dname, &dunit, "at", buf)) == 0) { if (strcmp(dname, "scbus")) { From 1af403658bfc821cc56e897abfa1a5fab80946ea Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 12:17:40 +0000 Subject: [PATCH 06/18] Fix off-by-one. Coverity CID: 1011375 Approved by: re (glebius) Sponsored by: FreeBSD Foundation --- usr.sbin/ctld/kernel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr.sbin/ctld/kernel.c b/usr.sbin/ctld/kernel.c index 0e002042192..dc55594d157 100644 --- a/usr.sbin/ctld/kernel.c +++ b/usr.sbin/ctld/kernel.c @@ -135,7 +135,7 @@ cctl_start_element(void *user_data, const char *name, const char **attr) devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; devlist->level++; - if ((u_int)devlist->level > (sizeof(devlist->cur_sb) / + if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]))) log_errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); From 846c5adb77d03960aaf816fe7cb26e4ef878a7d0 Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 12:19:48 +0000 Subject: [PATCH 07/18] Fix incorrect use of sizeof(). Coverity CID: 1011303 Approved by: re (glebius) Sponsored by: FreeBSD Foundation --- usr.sbin/ctladm/ctladm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c index d26bf72d140..5517284b090 100644 --- a/usr.sbin/ctladm/ctladm.c +++ b/usr.sbin/ctladm/ctladm.c @@ -700,7 +700,7 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt) } else if ((targ_port == -1) && (port_type == CTL_PORT_NONE)) port_type = CTL_PORT_ALL; - bzero(&entry, sizeof(&entry)); + bzero(&entry, sizeof(entry)); /* * These are needed for all but list/dump mode. From bd32ec0619939fa312963b659ee70c9e9cfc83bf Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 12:30:52 +0000 Subject: [PATCH 08/18] Fix two off-by-ones. Coverity CID: 1087999, 1011375 Approved by: re (glebius) Sponsored by: FreeBSD Foundation --- usr.sbin/ctladm/ctladm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c index 5517284b090..6f03de0c6ba 100644 --- a/usr.sbin/ctladm/ctladm.c +++ b/usr.sbin/ctladm/ctladm.c @@ -3442,7 +3442,7 @@ cctl_islist_start_element(void *user_data, const char *name, const char **attr) islist = (struct cctl_islist_data *)user_data; cur_conn = islist->cur_conn; islist->level++; - if ((u_int)islist->level > (sizeof(islist->cur_sb) / + if ((u_int)islist->level >= (sizeof(islist->cur_sb) / sizeof(islist->cur_sb[0]))) errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(islist->cur_sb) / sizeof(islist->cur_sb[0])); @@ -3848,7 +3848,7 @@ cctl_start_element(void *user_data, const char *name, const char **attr) devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; devlist->level++; - if ((u_int)devlist->level > (sizeof(devlist->cur_sb) / + if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]))) errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); From 5989c4cc166886c725bce31dc69700e4ec0f989f Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 13:28:45 +0000 Subject: [PATCH 09/18] Plug memory leaks. Coverity CID: 1087993, 1087992 Approved by: re (marius) Sponsored by: FreeBSD Foundation --- usr.sbin/ctld/login.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c index 4ee7c9798e2..042cf2ac80d 100644 --- a/usr.sbin/ctld/login.c +++ b/usr.sbin/ctld/login.c @@ -419,8 +419,9 @@ login_send_chap_c(struct pdu *request, const unsigned char id, keys_add(response_keys, "CHAP_C", chap_c); free(chap_c); keys_save(response_keys, response); - keys_delete(response_keys); pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); } static struct pdu * @@ -558,6 +559,7 @@ login_send_chap_success(struct pdu *request, keys_delete(request_keys); pdu_send(response); + pdu_delete(response); } static void From 9e55679baf0e20c463981416124e27b97ebd4062 Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 13:45:07 +0000 Subject: [PATCH 10/18] Fix memory overrun. Coverity CID: 1092478 Approved by: re (gjb) Sponsored by: FreeBSD Foundation --- usr.sbin/iscsid/discovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr.sbin/iscsid/discovery.c b/usr.sbin/iscsid/discovery.c index 639b71ac358..016f4449908 100644 --- a/usr.sbin/iscsid/discovery.c +++ b/usr.sbin/iscsid/discovery.c @@ -146,7 +146,7 @@ kernel_add(const struct connection *conn, const char *target) int error; memset(&isa, 0, sizeof(isa)); - memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa)); + memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa.isa_conf)); strlcpy(isa.isa_conf.isc_target, target, sizeof(isa.isa_conf.isc_target)); isa.isa_conf.isc_discovery = 0; From 0b50e359215310711f168266d57d0616c93e27b7 Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 13:48:08 +0000 Subject: [PATCH 11/18] Plug memory leak. Coverity CID: 1087991 Approved by: re (gjb) Sponsored by: FreeBSD Foundation --- usr.sbin/iscsid/discovery.c | 1 + 1 file changed, 1 insertion(+) diff --git a/usr.sbin/iscsid/discovery.c b/usr.sbin/iscsid/discovery.c index 016f4449908..b5731de9716 100644 --- a/usr.sbin/iscsid/discovery.c +++ b/usr.sbin/iscsid/discovery.c @@ -212,6 +212,7 @@ discovery(struct connection *conn) log_debugx("discovery done; logging out"); request = logout_new_request(conn); pdu_send(request); + pdu_delete(request); request = NULL; log_debugx("waiting for Logout Response"); From 8ba03960775074f74f501c4d273623d015300c10 Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 16:55:52 +0000 Subject: [PATCH 12/18] Tidy up, cache return value of a function, and add an assertion; shouldn't make any functional difference. Approved by: re (gjb) Sponsored by: FreeBSD Foundation --- sys/cam/ctl/ctl_frontend_iscsi.c | 38 +++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/sys/cam/ctl/ctl_frontend_iscsi.c b/sys/cam/ctl/ctl_frontend_iscsi.c index 1f310d926f8..831465cefc5 100644 --- a/sys/cam/ctl/ctl_frontend_iscsi.c +++ b/sys/cam/ctl/ctl_frontend_iscsi.c @@ -674,7 +674,7 @@ cfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *c struct iscsi_bhs_data_out *bhsdo; struct cfiscsi_session *cs; struct ctl_sg_entry ctl_sg_entry, *ctl_sglist; - size_t copy_len, off, buffer_offset; + size_t copy_len, len, off, buffer_offset; int ctl_sg_count; union ctl_io *io; @@ -732,7 +732,20 @@ cfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *c return (true); } + /* + * This is the offset within the PDU data segment, as opposed + * to buffer_offset, which is the offset within the task (SCSI + * command). + */ off = 0; + len = icl_pdu_data_segment_length(request); + + /* + * Iterate over the scatter/gather segments, filling them with data + * from the PDU data segment. Note that this can get called multiple + * times for one SCSI command; the cdw structure holds state for the + * scatter/gather list. + */ for (;;) { KASSERT(cdw->cdw_sg_index < ctl_sg_count, ("cdw->cdw_sg_index >= ctl_sg_count")); @@ -740,7 +753,8 @@ cfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *c cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr; cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len; } - copy_len = icl_pdu_data_segment_length(request) - off; + KASSERT(off <= len, ("len > off")); + copy_len = len - off; if (copy_len > cdw->cdw_sg_len) copy_len = cdw->cdw_sg_len; @@ -751,15 +765,27 @@ cfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *c io->scsiio.ext_data_filled += copy_len; if (cdw->cdw_sg_len == 0) { - if (cdw->cdw_sg_index == ctl_sg_count - 1) + /* + * End of current segment. + */ + if (cdw->cdw_sg_index == ctl_sg_count - 1) { + /* + * Last segment in scatter/gather list. + */ break; + } cdw->cdw_sg_index++; } - if (off == icl_pdu_data_segment_length(request)) + + if (off == len) { + /* + * End of PDU payload. + */ break; + } } - if (off < icl_pdu_data_segment_length(request)) { + if (len > off) { CFISCSI_SESSION_WARN(cs, "received too much data: got %zd bytes, " "expected %zd", icl_pdu_data_segment_length(request), off); cfiscsi_session_terminate(cs); @@ -2386,7 +2412,7 @@ cfiscsi_datamove_in(union ctl_io *io) /* * Can't stuff more data into the current PDU; * queue it. Note that's not enough to check - * for kern_data_resid == 0 instead; there + * for kern_data_resid == 0 instead; there * may be several Data-In PDUs for the final * call to cfiscsi_datamove(), and we want * to set the F flag only on the last of them. From 98c28062e0703580209edb8dca8949825be0a2c0 Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Wed, 9 Oct 2013 17:05:02 +0000 Subject: [PATCH 13/18] Remove redundant redeclarations of uart_s3c2410_class in sys/arm/s3c2xx0/uart_bus_s3c2410.c and uart_cpu_s3c2410.c, to silence two gcc warnings. Approved by: re (gjb) X-MFC-With: r252394 --- sys/arm/s3c2xx0/uart_bus_s3c2410.c | 1 - sys/arm/s3c2xx0/uart_cpu_s3c2410.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/sys/arm/s3c2xx0/uart_bus_s3c2410.c b/sys/arm/s3c2xx0/uart_bus_s3c2410.c index 974b3d939e5..41c750afb5c 100644 --- a/sys/arm/s3c2xx0/uart_bus_s3c2410.c +++ b/sys/arm/s3c2xx0/uart_bus_s3c2410.c @@ -35,7 +35,6 @@ static driver_t uart_s3c2410_driver = { sizeof(struct uart_softc), }; -extern struct uart_class uart_s3c2410_class; extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int uart_s3c2410_probe(device_t dev) diff --git a/sys/arm/s3c2xx0/uart_cpu_s3c2410.c b/sys/arm/s3c2xx0/uart_cpu_s3c2410.c index 835604be1fc..2ed036b48ad 100644 --- a/sys/arm/s3c2xx0/uart_cpu_s3c2410.c +++ b/sys/arm/s3c2xx0/uart_cpu_s3c2410.c @@ -47,8 +47,6 @@ extern struct uart_ops uart_s3c2410_ops; vm_offset_t s3c2410_uart_vaddr; unsigned int s3c2410_pclk; -extern struct uart_class uart_s3c2410_class; - int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) { From 02147e9cd0495e296bc58d832309681b4fba42d6 Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 17:06:03 +0000 Subject: [PATCH 14/18] Make the error handling more consistant. Shouldn't make any functional difference. Approved by: re (gjb) Sponsored by: FreeBSD Foundation --- sys/cam/ctl/ctl_frontend_iscsi.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/sys/cam/ctl/ctl_frontend_iscsi.c b/sys/cam/ctl/ctl_frontend_iscsi.c index 831465cefc5..52d30af81f3 100644 --- a/sys/cam/ctl/ctl_frontend_iscsi.c +++ b/sys/cam/ctl/ctl_frontend_iscsi.c @@ -288,8 +288,8 @@ cfiscsi_pdu_handle(struct icl_pdu *request) CFISCSI_SESSION_WARN(cs, "received PDU with unsupported " "opcode 0x%x; dropping connection", request->ip_bhs->bhs_opcode); - cfiscsi_session_terminate(cs); icl_pdu_free(request); + cfiscsi_session_terminate(cs); } } @@ -532,14 +532,16 @@ cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request) if (request->ip_data_len > 0 && cs->cs_immediate_data == false) { CFISCSI_SESSION_WARN(cs, "unsolicited data with " "ImmediateData=No; dropping connection"); - cfiscsi_session_terminate(cs); icl_pdu_free(request); + cfiscsi_session_terminate(cs); return; } io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref); if (io == NULL) { - CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io"); + CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io; " + "dropping connection"); icl_pdu_free(request); + cfiscsi_session_terminate(cs); return; } ctl_zero_io(io); @@ -579,10 +581,12 @@ cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request) refcount_acquire(&cs->cs_outstanding_ctl_pdus); error = ctl_queue(io); if (error != CTL_RETVAL_COMPLETE) { - CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error); + CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d; " + "dropping connection", error); ctl_free_io(io); refcount_release(&cs->cs_outstanding_ctl_pdus); icl_pdu_free(request); + cfiscsi_session_terminate(cs); } } @@ -600,8 +604,10 @@ cfiscsi_pdu_handle_task_request(struct icl_pdu *request) bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs; io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref); if (io == NULL) { - CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io"); + CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io;" + "dropping connection"); icl_pdu_free(request); + cfiscsi_session_terminate(cs); return; } ctl_zero_io(io); @@ -642,7 +648,10 @@ cfiscsi_pdu_handle_task_request(struct icl_pdu *request) response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { + CFISCSI_SESSION_WARN(cs, "failed to allocate memory; " + "dropping connection"); icl_pdu_free(request); + cfiscsi_session_terminate(cs); return; } bhstmr2 = (struct iscsi_bhs_task_management_response *) @@ -661,10 +670,12 @@ cfiscsi_pdu_handle_task_request(struct icl_pdu *request) refcount_acquire(&cs->cs_outstanding_ctl_pdus); error = ctl_queue(io); if (error != CTL_RETVAL_COMPLETE) { - CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error); + CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d; " + "dropping connection", error); ctl_free_io(io); refcount_release(&cs->cs_outstanding_ctl_pdus); icl_pdu_free(request); + cfiscsi_session_terminate(cs); } } @@ -859,8 +870,8 @@ cfiscsi_pdu_handle_data_out(struct icl_pdu *request) CFISCSI_SESSION_UNLOCK(cs); if (cdw == NULL) { CFISCSI_SESSION_WARN(cs, "data transfer tag 0x%x, initiator task tag " - "0x%x, not found", bhsdo->bhsdo_target_transfer_tag, - bhsdo->bhsdo_initiator_task_tag); + "0x%x, not found; dropping connection", + bhsdo->bhsdo_target_transfer_tag, bhsdo->bhsdo_initiator_task_tag); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; @@ -897,6 +908,7 @@ cfiscsi_pdu_handle_logout_request(struct icl_pdu *request) case BHSLR_REASON_CLOSE_CONNECTION: response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { + CFISCSI_SESSION_DEBUG(cs, "failed to allocate memory"); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; @@ -914,6 +926,8 @@ cfiscsi_pdu_handle_logout_request(struct icl_pdu *request) case BHSLR_REASON_REMOVE_FOR_RECOVERY: response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { + CFISCSI_SESSION_WARN(cs, + "failed to allocate memory; dropping connection"); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; @@ -985,7 +999,7 @@ cfiscsi_callout(void *context) cp = icl_pdu_new_bhs(cs->cs_conn, M_NOWAIT); if (cp == NULL) { - CFISCSI_SESSION_WARN(cs, "failed to allocate PDU"); + CFISCSI_SESSION_WARN(cs, "failed to allocate memory"); return; } bhsni = (struct iscsi_bhs_nop_in *)cp->ip_bhs; From c9fc60beee8b188b70894c502e2a9f2332eb0c89 Mon Sep 17 00:00:00 2001 From: Glen Barber Date: Wed, 9 Oct 2013 17:07:20 +0000 Subject: [PATCH 15/18] Revert r256095, r256120 (partial), r256121: r256095: - Add gnu/usr.bin/rcs back to the base system. r256120: - Add WITHOUT_RCS back to src.conf.5. r256121: - Remove UPDATING entry regarding gnu/usr.bin/rcs removal. Requested by: many Approved by: re (marius) Discussed with: core --- ObsoleteFiles.inc | 23 - UPDATING | 4 - gnu/usr.bin/Makefile | 5 + gnu/usr.bin/rcs/CREDITS | 24 + gnu/usr.bin/rcs/Makefile | 3 + gnu/usr.bin/rcs/Makefile.inc | 3 + gnu/usr.bin/rcs/NEWS | 548 ++++++ gnu/usr.bin/rcs/REFS | 90 + gnu/usr.bin/rcs/ci/Makefile | 8 + gnu/usr.bin/rcs/ci/ci.1 | 898 ++++++++++ gnu/usr.bin/rcs/ci/ci.c | 1318 +++++++++++++++ gnu/usr.bin/rcs/co/Makefile | 8 + gnu/usr.bin/rcs/co/co.1 | 736 ++++++++ gnu/usr.bin/rcs/co/co.c | 826 +++++++++ gnu/usr.bin/rcs/doc/rcs.ms | 1518 +++++++++++++++++ gnu/usr.bin/rcs/doc/rcs_func.ms | 95 ++ gnu/usr.bin/rcs/ident/Makefile | 8 + gnu/usr.bin/rcs/ident/ident.1 | 182 ++ gnu/usr.bin/rcs/ident/ident.c | 270 +++ gnu/usr.bin/rcs/lib/Makefile | 14 + gnu/usr.bin/rcs/lib/conf.h | 400 +++++ gnu/usr.bin/rcs/lib/maketime.c | 344 ++++ gnu/usr.bin/rcs/lib/maketime.h | 39 + gnu/usr.bin/rcs/lib/merger.c | 148 ++ gnu/usr.bin/rcs/lib/partime.c | 701 ++++++++ gnu/usr.bin/rcs/lib/partime.h | 71 + gnu/usr.bin/rcs/lib/rcsbase.h | 762 +++++++++ gnu/usr.bin/rcs/lib/rcsedit.c | 1958 ++++++++++++++++++++++ gnu/usr.bin/rcs/lib/rcsfcmp.c | 354 ++++ gnu/usr.bin/rcs/lib/rcsfnms.c | 1132 +++++++++++++ gnu/usr.bin/rcs/lib/rcsgen.c | 681 ++++++++ gnu/usr.bin/rcs/lib/rcskeep.c | 452 +++++ gnu/usr.bin/rcs/lib/rcskeys.c | 186 ++ gnu/usr.bin/rcs/lib/rcslex.c | 1568 +++++++++++++++++ gnu/usr.bin/rcs/lib/rcsmap.c | 69 + gnu/usr.bin/rcs/lib/rcsrev.c | 911 ++++++++++ gnu/usr.bin/rcs/lib/rcssyn.c | 681 ++++++++ gnu/usr.bin/rcs/lib/rcstime.c | 191 +++ gnu/usr.bin/rcs/lib/rcsutil.c | 1398 +++++++++++++++ gnu/usr.bin/rcs/lib/version.c | 2 + gnu/usr.bin/rcs/merge/Makefile | 8 + gnu/usr.bin/rcs/merge/merge.1 | 137 ++ gnu/usr.bin/rcs/merge/merge.c | 113 ++ gnu/usr.bin/rcs/rcs/Makefile | 10 + gnu/usr.bin/rcs/rcs/rcs.1 | 454 +++++ gnu/usr.bin/rcs/rcs/rcs.c | 1629 ++++++++++++++++++ gnu/usr.bin/rcs/rcs/rcsfile.5 | 425 +++++ gnu/usr.bin/rcs/rcs/rcsintro.1 | 302 ++++ gnu/usr.bin/rcs/rcsclean/Makefile | 8 + gnu/usr.bin/rcs/rcsclean/rcsclean.1 | 203 +++ gnu/usr.bin/rcs/rcsclean/rcsclean.c | 333 ++++ gnu/usr.bin/rcs/rcsdiff/Makefile | 8 + gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 | 158 ++ gnu/usr.bin/rcs/rcsdiff/rcsdiff.c | 480 ++++++ gnu/usr.bin/rcs/rcsfreeze/Makefile | 7 + gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.1 | 68 + gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh | 99 ++ gnu/usr.bin/rcs/rcsmerge/Makefile | 8 + gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 | 189 +++ gnu/usr.bin/rcs/rcsmerge/rcsmerge.c | 286 ++++ gnu/usr.bin/rcs/rcstest | 454 +++++ gnu/usr.bin/rcs/rlog/Makefile | 8 + gnu/usr.bin/rcs/rlog/rlog.1 | 318 ++++ gnu/usr.bin/rcs/rlog/rlog.c | 1290 ++++++++++++++ share/doc/psd/13.rcs/Makefile | 6 + share/doc/psd/13.rcs/Makefile.inc | 5 + share/doc/psd/13.rcs/rcs/Makefile | 7 + share/doc/psd/13.rcs/rcs_func/Makefile | 6 + share/doc/psd/Makefile | 1 + share/man/man5/src.conf.5 | 5 + share/mk/bsd.own.mk | 1 + tools/build/mk/OptionalObsoleteFiles.inc | 25 + tools/build/options/WITHOUT_RCS | 4 + 73 files changed, 25657 insertions(+), 27 deletions(-) create mode 100644 gnu/usr.bin/rcs/CREDITS create mode 100644 gnu/usr.bin/rcs/Makefile create mode 100644 gnu/usr.bin/rcs/Makefile.inc create mode 100644 gnu/usr.bin/rcs/NEWS create mode 100644 gnu/usr.bin/rcs/REFS create mode 100644 gnu/usr.bin/rcs/ci/Makefile create mode 100644 gnu/usr.bin/rcs/ci/ci.1 create mode 100644 gnu/usr.bin/rcs/ci/ci.c create mode 100644 gnu/usr.bin/rcs/co/Makefile create mode 100644 gnu/usr.bin/rcs/co/co.1 create mode 100644 gnu/usr.bin/rcs/co/co.c create mode 100644 gnu/usr.bin/rcs/doc/rcs.ms create mode 100644 gnu/usr.bin/rcs/doc/rcs_func.ms create mode 100644 gnu/usr.bin/rcs/ident/Makefile create mode 100644 gnu/usr.bin/rcs/ident/ident.1 create mode 100644 gnu/usr.bin/rcs/ident/ident.c create mode 100644 gnu/usr.bin/rcs/lib/Makefile create mode 100644 gnu/usr.bin/rcs/lib/conf.h create mode 100644 gnu/usr.bin/rcs/lib/maketime.c create mode 100644 gnu/usr.bin/rcs/lib/maketime.h create mode 100644 gnu/usr.bin/rcs/lib/merger.c create mode 100644 gnu/usr.bin/rcs/lib/partime.c create mode 100644 gnu/usr.bin/rcs/lib/partime.h create mode 100644 gnu/usr.bin/rcs/lib/rcsbase.h create mode 100644 gnu/usr.bin/rcs/lib/rcsedit.c create mode 100644 gnu/usr.bin/rcs/lib/rcsfcmp.c create mode 100644 gnu/usr.bin/rcs/lib/rcsfnms.c create mode 100644 gnu/usr.bin/rcs/lib/rcsgen.c create mode 100644 gnu/usr.bin/rcs/lib/rcskeep.c create mode 100644 gnu/usr.bin/rcs/lib/rcskeys.c create mode 100644 gnu/usr.bin/rcs/lib/rcslex.c create mode 100644 gnu/usr.bin/rcs/lib/rcsmap.c create mode 100644 gnu/usr.bin/rcs/lib/rcsrev.c create mode 100644 gnu/usr.bin/rcs/lib/rcssyn.c create mode 100644 gnu/usr.bin/rcs/lib/rcstime.c create mode 100644 gnu/usr.bin/rcs/lib/rcsutil.c create mode 100644 gnu/usr.bin/rcs/lib/version.c create mode 100644 gnu/usr.bin/rcs/merge/Makefile create mode 100644 gnu/usr.bin/rcs/merge/merge.1 create mode 100644 gnu/usr.bin/rcs/merge/merge.c create mode 100644 gnu/usr.bin/rcs/rcs/Makefile create mode 100644 gnu/usr.bin/rcs/rcs/rcs.1 create mode 100644 gnu/usr.bin/rcs/rcs/rcs.c create mode 100644 gnu/usr.bin/rcs/rcs/rcsfile.5 create mode 100644 gnu/usr.bin/rcs/rcs/rcsintro.1 create mode 100644 gnu/usr.bin/rcs/rcsclean/Makefile create mode 100644 gnu/usr.bin/rcs/rcsclean/rcsclean.1 create mode 100644 gnu/usr.bin/rcs/rcsclean/rcsclean.c create mode 100644 gnu/usr.bin/rcs/rcsdiff/Makefile create mode 100644 gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 create mode 100644 gnu/usr.bin/rcs/rcsdiff/rcsdiff.c create mode 100644 gnu/usr.bin/rcs/rcsfreeze/Makefile create mode 100644 gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.1 create mode 100644 gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh create mode 100644 gnu/usr.bin/rcs/rcsmerge/Makefile create mode 100644 gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 create mode 100644 gnu/usr.bin/rcs/rcsmerge/rcsmerge.c create mode 100755 gnu/usr.bin/rcs/rcstest create mode 100644 gnu/usr.bin/rcs/rlog/Makefile create mode 100644 gnu/usr.bin/rcs/rlog/rlog.1 create mode 100644 gnu/usr.bin/rcs/rlog/rlog.c create mode 100644 share/doc/psd/13.rcs/Makefile create mode 100644 share/doc/psd/13.rcs/Makefile.inc create mode 100644 share/doc/psd/13.rcs/rcs/Makefile create mode 100644 share/doc/psd/13.rcs/rcs_func/Makefile create mode 100644 tools/build/options/WITHOUT_RCS diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index dcc4c4c0c3c..9f3fbb9a848 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -38,29 +38,6 @@ # xargs -n1 | sort | uniq -d; # done -# 20131015: removal of RCS from base -OLD_FILES+=usr/bin/ci -OLD_FILES+=usr/bin/co -OLD_FILES+=usr/bin/ident -OLD_FILES+=usr/bin/merge -OLD_FILES+=usr/bin/rcs -OLD_FILES+=usr/bin/rcsclean -OLD_FILES+=usr/bin/rcsdiff -OLD_FILES+=usr/bin/rcsfreeze -OLD_FILES+=usr/bin/rcsmerge -OLD_FILES+=usr/bin/rlog -OLD_FILES+=usr/share/man/man1/ci.1.gz -OLD_FILES+=usr/share/man/man1/co.1.gz -OLD_FILES+=usr/share/man/man1/ident.1.gz -OLD_FILES+=usr/share/man/man1/merge.1.gz -OLD_FILES+=usr/share/man/man1/rcs.1.gz -OLD_FILES+=usr/share/man/man1/rcsclean.1.gz -OLD_FILES+=usr/share/man/man1/rcsdiff.1.gz -OLD_FILES+=usr/share/man/man1/rcsfreeze.1.gz -OLD_FILES+=usr/share/man/man1/rcsintro.1.gz -OLD_FILES+=usr/share/man/man1/rcsmerge.1.gz -OLD_FILES+=usr/share/man/man1/rlog.1.gz -OLD_FILES+=usr/share/man/man5/rcsfile.5.gz # 20131001: ar and ranlib from binutils not used OLD_FILES+=usr/bin/gnu-ar OLD_FILES+=usr/bin/gnu-ranlib diff --git a/UPDATING b/UPDATING index e9f0251e068..067f5a01b71 100644 --- a/UPDATING +++ b/UPDATING @@ -31,10 +31,6 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW: disable the most expensive debugging functionality run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) -20131006: - RCS has been removed from the base system. If you need RCS - install either devel/rcs or devel/rcs57. - 20130930: BIND has been removed from the base system. If all you need is a local resolver, simply enable and start the local_unbound diff --git a/gnu/usr.bin/Makefile b/gnu/usr.bin/Makefile index 8ece263b33e..663107c3947 100644 --- a/gnu/usr.bin/Makefile +++ b/gnu/usr.bin/Makefile @@ -12,6 +12,7 @@ SUBDIR= ${_binutils} \ ${_gperf} \ grep \ ${_groff} \ + ${_rcs} \ sdiff \ send-pr \ ${_texinfo} @@ -31,6 +32,10 @@ _dtc= dtc _texinfo= texinfo .endif +.if ${MK_RCS} != "no" +_rcs= rcs +.endif + .if ${MK_BINUTILS} != "no" _binutils= binutils .endif diff --git a/gnu/usr.bin/rcs/CREDITS b/gnu/usr.bin/rcs/CREDITS new file mode 100644 index 00000000000..589e8b68509 --- /dev/null +++ b/gnu/usr.bin/rcs/CREDITS @@ -0,0 +1,24 @@ +RCS was designed and built by Walter F. Tichy of Purdue University. +RCS version 3 was released in 1983. + +Adam Hammer, Thomas Narten, and Daniel Trinkle of Purdue supported RCS through +version 4.3, released in 1990. Guy Harris of Sun contributed many porting +fixes. Paul Eggert of System Development Corporation contributed bug fixes +and tuneups. Jay Lepreau contributed 4.3BSD support. + +Paul Eggert of Twin Sun wrote the changes for RCS versions 5.5 and 5.6 (1991). +Rich Braun of Kronos and Andy Glew of Intel contributed ideas for new options. +Bill Hahn of Stratus contributed ideas for setuid support. +Ideas for piece tables came from Joe Berkovitz of Stratus and Walter F. Tichy. +Matt Cross of Stratus contributed test case ideas. +Adam Hammer of Purdue QAed. + +Paul Eggert wrote most of the changes for this version of RCS, +currently in beta test. K. Richard Pixley of Cygnus Support +contributed several bug fixes. Robert Lupton of Princeton +and Daniel Trinkle contributed ideas for $Name expansion. +Brendan Kehoe of Cygnus Support suggested rlog's -N option. +Paul D. Smith of Data General suggested improvements in option +and error processing. Adam Hammer of Purdue QAed. + +$FreeBSD$ diff --git a/gnu/usr.bin/rcs/Makefile b/gnu/usr.bin/rcs/Makefile new file mode 100644 index 00000000000..4a9fd0838a0 --- /dev/null +++ b/gnu/usr.bin/rcs/Makefile @@ -0,0 +1,3 @@ +SUBDIR= lib ci co ident merge rcs rcsclean rcsdiff rcsmerge rlog rcsfreeze + +.include diff --git a/gnu/usr.bin/rcs/Makefile.inc b/gnu/usr.bin/rcs/Makefile.inc new file mode 100644 index 00000000000..a46437ad8c8 --- /dev/null +++ b/gnu/usr.bin/rcs/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +LIBRCS= ${.OBJDIR}/../lib/librcs.a diff --git a/gnu/usr.bin/rcs/NEWS b/gnu/usr.bin/rcs/NEWS new file mode 100644 index 00000000000..99208064bd3 --- /dev/null +++ b/gnu/usr.bin/rcs/NEWS @@ -0,0 +1,548 @@ +Recent changes to RCS (and possible future changes) + + $FreeBSD$ + + Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + + This file is part of RCS. + + RCS is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + RCS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with RCS; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + + +Here is a brief summary of user-visible changes since 5.6. + + New options: + `-kb' supports binary files. + `-T' preserves the modification time of RCS files. + `-V' prints the version number. + `-zLT' causes RCS to use local time in working files and logs. + `rcsclean -n' outputs what rcsclean would do, without actually doing it. + `rlog -N' omits symbolic names. + There is a new keyword `Name'. + Inserted log lines now have the same prefix as the preceding `$Log' line. + +Most changes for RCS version 5.7 are to fix bugs and improve portability. +RCS now conforms to GNU configuration standards and to Posix 1003.1b-1993. + + +Features new to RCS version 5.7, and possibly incompatible +in minor ways with previous practice, include: + + Inserted log lines now have the same prefix as the preceding `$Log' line. + E.g. if a $Log line starts with `// $Log', log lines are prefixed with `// '. + RCS still records the (now obsolescent) comment leader inside RCS files, + but it ignores the comment leader unless it is emulating older RCS versions. + If you plan to access a file with both old and new versions of RCS, + make sure its comment leader matches its `$Log' line prefix. + For backwards compatibility with older versions of RCS, + if the log prefix is `/*' or `(*' surrounded by optional white space, + inserted log lines contain ` *' instead of `/*' or `(*'; + however, this usage is obsolescent and should not be relied on. + + $Log string `Revision' times now use the same format as other times. + + Log lines are now inserted even if -kk is specified; this simplifies merging. + + ci's -rR option (with a nonempty R) now just specifies a revision number R. + In some beta versions, it also reestablished the default behavior of + releasing a lock and removing the working file. + Now, only the bare -r option does this. + + With an empty extension, any appearance of a directory named `RCS' + in a pathname identifies the pathname as being that of an RCS file. + For example, `a/RCS/b/c' is now an RCS file with an empty extension. + Formerly, `RCS' had to be the last directory in the pathname. + + rlog's -d option by default now uses exclusive time ranges. + E.g. `rlog -d" + + for software engineering; e.g. see + . + + + + for configuration management + + + + for CVS (see below) + + +RCS and related GNU project software + + + The RCS project distribution directory also contains beta versions, + ports, and prebuilt documentation. + + + The GNU project distribution directory contains: + diffutils-N-tar.gz + the latest diffutils release; recommended for RCS + emacs-N-tar.gz + The latest Emacs release contains VC, a version-control package + that makes RCS easier to use. + make-N-tar.gz + GNU Make, which can automatically build from RCS files. + rcs-N-tar.gz + the latest RCS release + cvs-N-tar.gz + the latest official CVS release (see below) + + DOS, OS/2 ports + NT port + + +CVS + +CVS, the Concurrent Versions System, keeps tracks of source changes +made by groups of developers working on the same files concurrently, +allowing them to resync as needed. + + + + These pages have useful information about CVS. + + + CVS 1.3 is the latest released version. + + + CVS 1.4 is in alpha test, but it is recommended if you are installing CVS + for the first time, or on a recent operating system. + + DOS, OS/2 ports + NT port + + + Cyclic CVS adds network transparency to CVS; it supports efficient, + reliable, and authenticated repository access via TCP/IP. + + +Other software that uses RCS + + + Aegis manages revisions, baselines, mandatory reviews, and mandatory testing. + + + BCS, the Baseline Configuration System, + manages revisions, baselines, and staging areas. + + + ODE, the Open Software Foundation Development Environment, + manages revisions, builds, and sandboxes. + OSF uses it for their own development. + + + Odin, a `make' replacement, can build directly from arbitrary revisions + without requiring checkouts of working copies. It also handles + parallel builds on multiple remote hosts and of multiple variants. diff --git a/gnu/usr.bin/rcs/ci/Makefile b/gnu/usr.bin/rcs/ci/Makefile new file mode 100644 index 00000000000..2fbb74f4238 --- /dev/null +++ b/gnu/usr.bin/rcs/ci/Makefile @@ -0,0 +1,8 @@ +PROG= ci +SRCS= ci.c +CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/ci/ci.1 b/gnu/usr.bin/rcs/ci/ci.1 new file mode 100644 index 00000000000..1378af222cb --- /dev/null +++ b/gnu/usr.bin/rcs/ci/ci.1 @@ -0,0 +1,898 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds i \&\s-1ISO\s0 +.ds r \&\s-1RCS\s0 +.ds u \&\s-1UTC\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH CI 1 \*(Dt GNU +.SH NAME +ci \- check in RCS revisions +.SH SYNOPSIS +.B ci +.RI [ options ] " file " .\|.\|. +.SH DESCRIPTION +.B ci +stores new revisions into \*r files. +Each pathname matching an \*r suffix +is taken to be an \*r file. +All others +are assumed to be working files containing new revisions. +.B ci +deposits the contents of each working file +into the corresponding \*r file. +If only a working file is given, +.B ci +tries to find the corresponding \*r file in an \*r subdirectory +and then in the working file's directory. +For more details, see +.SM "FILE NAMING" +below. +.PP +For +.B ci +to work, the caller's login must be on the access list, +except if the access list is empty or the caller is the superuser or the +owner of the file. +To append a new revision to an existing branch, the tip revision on +that branch must be locked by the caller. Otherwise, only a +new branch can be created. This restriction is not enforced +for the owner of the file if non-strict locking is used +(see +.BR rcs (1)). +A lock held by someone else can be broken with the +.B rcs +command. +.PP +Unless the +.B \-f +option is given, +.B ci +checks whether the revision to be deposited differs from the preceding one. +If not, instead of creating a new revision +.B ci +reverts to the preceding one. +To revert, ordinary +.B ci +removes the working file and any lock; +.B "ci\ \-l" +keeps and +.B "ci\ \-u" +removes any lock, and then they both generate a new working file much as if +.B "co\ \-l" +or +.B "co\ \-u" +had been applied to the preceding revision. +When reverting, any +.B \-n +and +.B \-s +options apply to the preceding revision. +.PP +For each revision deposited, +.B ci +prompts for a log message. +The log message should summarize the change and must be terminated by +end-of-file or by a line containing +.BR \&. "\ by" +itself. +If several files are checked in +.B ci +asks whether to reuse the +previous log message. +If the standard input is not a terminal, +.B ci +suppresses the prompt +and uses the same log message for all files. +See also +.BR \-m . +.PP +If the \*r file does not exist, +.B ci +creates it and +deposits the contents of the working file as the initial revision +(default number: +.BR 1.1 ). +The access list is initialized to empty. +Instead of the log message, +.B ci +requests descriptive text (see +.B \-t +below). +.PP +The number +.I rev +of the deposited revision can be given by any of the options +.BR \-f , +.BR \-i , +.BR \-I , +.BR \-j , +.BR \-k , +.BR \-l , +.BR \-M , +.BR \-q , +.BR \-r , +or +.BR \-u . +.I rev +can be symbolic, numeric, or mixed. +Symbolic names in +.I rev +must already be defined; +see the +.B \-n +and +.B \-N +options for assigning names during checkin. +If +.I rev +is +.BR $ , +.B ci +determines the revision number from keyword values in the working file. +.PP +If +.I rev +begins with a period, +then the default branch (normally the trunk) is prepended to it. +If +.I rev +is a branch number followed by a period, +then the latest revision on that branch is used. +.PP +If +.I rev +is a revision number, it must be higher than the latest +one on the branch to which +.I rev +belongs, or must start a new branch. +.PP +If +.I rev +is a branch rather than a revision number, +the new revision is appended to that branch. The level number is obtained +by incrementing the tip revision number of that branch. +If +.I rev +indicates a non-existing branch, +that branch is created with the initial revision numbered +.IB rev .1\f1.\fP +.br +.ne 8 +.PP +If +.I rev +is omitted, +.B ci +tries to derive the new revision number from +the caller's last lock. If the caller has locked the tip revision of a branch, +the new revision is appended to that branch. +The new revision number is obtained +by incrementing the tip revision number. +If the caller locked a non-tip revision, a new branch is started at +that revision by incrementing the highest branch number at that revision. +The default initial branch and level numbers are +.BR 1 . +.PP +If +.I rev +is omitted and the caller has no lock, but owns +the file and locking +is not set to +.IR strict , +then the revision is appended to the +default branch (normally the trunk; see the +.B \-b +option of +.BR rcs (1)). +.PP +Exception: On the trunk, revisions can be appended to the end, but +not inserted. +.SH OPTIONS +.TP +.BI \-r rev +Check in revision +.IR rev . +.TP +.BR \-r +The bare +.B \-r +option (without any revision) has an unusual meaning in +.BR ci . +With other \*r commands, a bare +.B \-r +option specifies the most recent revision on the default branch, +but with +.BR ci , +a bare +.B \-r +option reestablishes the default behavior of releasing a lock and +removing the working file, and is used to override any default +.B \-l +or +.B \-u +options established by shell aliases or scripts. +.TP +.BR \-l [\f2rev\fP] +works like +.BR \-r , +except it performs an additional +.B "co\ \-l" +for the +deposited revision. Thus, the deposited revision is immediately +checked out again and locked. +This is useful for saving a revision although one wants to continue +editing it after the checkin. +.TP +.BR \-u [\f2rev\fP] +works like +.BR \-l , +except that the deposited revision is not locked. +This lets one read the working file +immediately after checkin. +.RS +.PP +The +.BR \-l , +bare +.BR \-r , +and +.B \-u +options are mutually exclusive and silently override each other. +For example, +.B "ci\ \-u\ \-r" +is equivalent to +.B "ci\ \-r" +because bare +.B \-r +overrides +.BR \-u . +.RE +.TP +.BR \-f [\f2rev\fP] +forces a deposit; the new revision is deposited even it is not different +from the preceding one. +.TP +.BR \-k [\f2rev\fP] +searches the working file for keyword values to determine its revision number, +creation date, state, and author (see +.BR co (1)), +and assigns these +values to the deposited revision, rather than computing them locally. +It also generates a default login message noting the login of the caller +and the actual checkin date. +This option is useful for software distribution. A revision that is sent to +several sites should be checked in with the +.B \-k +option at these sites to +preserve the original number, date, author, and state. +The extracted keyword values and the default log message can be overridden +with the options +.BR \-d , +.BR \-m , +.BR \-s , +.BR \-w , +and any option that carries a revision number. +.TP +.BR \-q [\f2rev\fP] +quiet mode; diagnostic output is not printed. +A revision that is not different from the preceding one is not deposited, +unless +.B \-f +is given. +.TP +.BR \-i [\f2rev\fP] +initial checkin; report an error if the \*r file already exists. +This avoids race conditions in certain applications. +.TP +.BR \-j [\f2rev\fP] +just checkin and do not initialize; +report an error if the \*r file does not already exist. +.TP +.BR \-I [\f2rev\fP] +interactive mode; +the user is prompted and questioned +even if the standard input is not a terminal. +.TP +.BR \-d "[\f2date\fP]" +uses +.I date +for the checkin date and time. +The +.I date +is specified in free format as explained in +.BR co (1). +This is useful for lying about the checkin date, and for +.B \-k +if no date is available. +If +.I date +is empty, the working file's time of last modification is used. +.TP +.BR \-M [\f2rev\fP] +Set the modification time on any new working file +to be the date of the retrieved revision. +For example, +.BI "ci\ \-d\ \-M\ \-u" "\ f" +does not alter +.IR f 's +modification time, even if +.IR f 's +contents change due to keyword substitution. +Use this option with care; it can confuse +.BR make (1). +.TP +.BI \-m "msg" +uses the string +.I msg +as the log message for all revisions checked in. +By convention, log messages that start with +.B # +are comments and are ignored by programs like GNU Emacs's +.B vc +package. +Also, log messages that start with +.BI { clumpname } +(followed by white space) are meant to be clumped together if possible, +even if they are associated with different files; the +.BI { clumpname } +label is used only for clumping, +and is not considered to be part of the log message itself. +.TP +.BI \-n "name" +assigns the symbolic name +.I name +to the number of the checked-in revision. +.B ci +prints an error message if +.I name +is already assigned to another +number. +.TP +.BI \-N "name" +same as +.BR \-n , +except that it overrides a previous assignment of +.IR name . +.TP +.BI \-s "state" +sets the state of the checked-in revision to the identifier +.IR state . +The default state is +.BR Exp . +.TP +.BI \-t file +writes descriptive text from the contents of the named +.I file +into the \*r file, +deleting the existing text. +The +.I file +cannot begin with +.BR \- . +.TP +.BI \-t\- string +Write descriptive text from the +.I string +into the \*r file, deleting the existing text. +.RS +.PP +The +.B \-t +option, in both its forms, has effect only during an initial checkin; +it is silently ignored otherwise. +.PP +During the initial checkin, if +.B \-t +is not given, +.B ci +obtains the text from standard input, +terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +The user is prompted for the text if interaction is possible; see +.BR \-I . +.PP +For backward compatibility with older versions of \*r, a bare +.B \-t +option is ignored. +.RE +.TP +.B \-T +Set the \*r file's modification time to the new revision's time +if the former precedes the latter and there is a new revision; +preserve the \*r file's modification time otherwise. +If you have locked a revision, +.B ci +usually updates the \*r file's modification time to the current time, +because the lock is stored in the \*r file +and removing the lock requires changing the \*r file. +This can create an \*r file newer than the working file in one of two ways: +first, +.B "ci\ \-M" +can create a working file with a date before the current time; +second, when reverting to the previous revision +the \*r file can change while the working file remains unchanged. +These two cases can cause excessive recompilation caused by a +.BR make (1) +dependency of the working file on the \*r file. +The +.B \-T +option inhibits this recompilation by lying about the \*r file's date. +Use this option with care; it can suppress recompilation even when +a checkin of one working file should affect +another working file associated with the same \*r file. +For example, suppose the \*r file's time is 01:00, +the (changed) working file's time is 02:00, +some other copy of the working file has a time of 03:00, +and the current time is 04:00. +Then +.B "ci\ \-d\ \-T" +sets the \*r file's time to 02:00 instead of the usual 04:00; +this causes +.BR make (1) +to think (incorrectly) that the other copy is newer than the \*r file. +.TP +.BI \-w "login" +uses +.I login +for the author field of the deposited revision. +Useful for lying about the author, and for +.B \-k +if no author is available. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.TP +.BI \-x "suffixes" +specifies the suffixes for \*r files. +A nonempty suffix matches any pathname ending in the suffix. +An empty suffix matches any pathname of the form +.BI RCS/ path +or +.IB path1 /RCS/ path2. +The +.B \-x +option can specify a list of suffixes +separated by +.BR / . +For example, +.B \-x,v/ +specifies two suffixes: +.B ,v +and the empty suffix. +If two or more suffixes are specified, +they are tried in order when looking for an \*r file; +the first one that works is used for that file. +If no \*r file is found but an \*r file can be created, +the suffixes are tried in order +to determine the new \*r file's name. +The default for +.IR suffixes +is installation-dependent; normally it is +.B ,v/ +for hosts like Unix that permit commas in filenames, +and is empty (i.e. just the empty suffix) for other hosts. +.TP +.BI \-z zone +specifies the date output format in keyword substitution, +and specifies the default time zone for +.I date +in the +.BI \-d date +option. +The +.I zone +should be empty, a numeric \*u offset, or the special string +.B LT +for local time. +The default is an empty +.IR zone , +which uses the traditional \*r format of \*u without any time zone indication +and with slashes separating the parts of the date; +otherwise, times are output in \*i 8601 format with time zone indication. +For example, if local time is January 11, 1990, 8pm Pacific Standard Time, +eight hours west of \*u, +then the time is output as follows: +.RS +.LP +.RS +.nf +.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u +.ne 4 +\f2option\fP \f2time output\fP +\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP +\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP +\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP +.ta 4n +4n +4n +4n +.fi +.RE +.LP +The +.B \-z +option does not affect dates stored in \*r files, +which are always \*u. +.SH "FILE NAMING" +Pairs of \*r files and working files can be specified in three ways +(see also the +example section). +.PP +1) Both the \*r file and the working file are given. The \*r pathname is of +the form +.IB path1 / workfileX +and the working pathname is of the form +.IB path2 / workfile +where +.IB path1 / +and +.IB path2 / +are (possibly different or empty) paths, +.I workfile +is a filename, and +.I X +is an \*r suffix. +If +.I X +is empty, +.IB path1 / +must start with +.B RCS/ +or must contain +.BR /RCS/ . +.PP +2) Only the \*r file is given. Then the working file is created in the current +directory and its name is derived from the name of the \*r file +by removing +.IB path1 / +and the suffix +.IR X . +.PP +3) Only the working file is given. +Then +.B ci +considers each \*r suffix +.I X +in turn, looking for an \*r file of the form +.IB path2 /RCS/ workfileX +or (if the former is not found and +.I X +is nonempty) +.IB path2 / workfileX. +.PP +If the \*r file is specified without a path in 1) and 2), +.B ci +looks for the \*r file first in the directory +.B ./RCS +and then in the current +directory. +.PP +.B ci +reports an error if an attempt to open an \*r file fails for an unusual reason, +even if the \*r file's pathname is just one of several possibilities. +For example, to suppress use of \*r commands in a directory +.IR d , +create a regular file named +.IB d /RCS +so that casual attempts to use \*r commands in +.I d +fail because +.IB d /RCS +is not a directory. +.SH EXAMPLES +Suppose +.B ,v +is an \*r suffix and the current directory contains a subdirectory +.B RCS +with an \*r file +.BR io.c,v . +Then each of the following commands check in a copy of +.B io.c +into +.B RCS/io.c,v +as the latest revision, removing +.BR io.c . +.LP +.RS +.nf +.ft 3 +ci io.c; ci RCS/io.c,v; ci io.c,v; +ci io.c RCS/io.c,v; ci io.c io.c,v; +ci RCS/io.c,v io.c; ci io.c,v io.c; +.ft +.fi +.RE +.PP +Suppose instead that the empty suffix +is an \*r suffix and the current directory contains a subdirectory +.B RCS +with an \*r file +.BR io.c . +The each of the following commands checks in a new revision. +.LP +.RS +.nf +.ft 3 +ci io.c; ci RCS/io.c; +ci io.c RCS/io.c; +ci RCS/io.c io.c; +.ft +.fi +.RE +.SH "FILE MODES" +An \*r file created by +.B ci +inherits the read and execute permissions +from the working file. If the \*r file exists already, +.B ci +preserves its read and execute permissions. +.B ci +always turns off all write permissions of \*r files. +.SH FILES +Temporary files are created in the directory containing +the working file, and also in the temporary directory (see +.B \s-1TMPDIR\s0 +under +.BR \s-1ENVIRONMENT\s0 ). +A semaphore file or files are created in the directory containing the \*r file. +With a nonempty suffix, the semaphore names begin with +the first character of the suffix; therefore, do not specify an suffix +whose first character could be that of a working filename. +With an empty suffix, the semaphore names end with +.B _ +so working filenames should not end in +.BR _ . +.PP +.B ci +never changes an \*r or working file. +Normally, +.B ci +unlinks the file and creates a new one; +but instead of breaking a chain of one or more symbolic links to an \*r file, +it unlinks the destination file instead. +Therefore, +.B ci +breaks any hard or symbolic links to any working file it changes; +and hard links to \*r files are ineffective, +but symbolic links to \*r files are preserved. +.PP +The effective user must be able to +search and write the directory containing the \*r file. +Normally, the real user must be able to +read the \*r and working files +and to search and write the directory containing the working file; +however, some older hosts +cannot easily switch between real and effective users, +so on these hosts the effective user is used for all accesses. +The effective user is the same as the real user +unless your copies of +.B ci +and +.B co +have setuid privileges. +As described in the next section, +these privileges yield extra security if +the effective user owns all \*r files and directories, +and if only the effective user can write \*r directories. +.PP +Users can control access to \*r files by setting the permissions +of the directory containing the files; only users with write access +to the directory can use \*r commands to change its \*r files. +For example, in hosts that allow a user to belong to several groups, +one can make a group's \*r directories writable to that group only. +This approach suffices for informal projects, +but it means that any group member can arbitrarily change the group's \*r files, +and can even remove them entirely. +Hence more formal projects sometimes distinguish between an \*r administrator, +who can change the \*r files at will, and other project members, +who can check in new revisions but cannot otherwise change the \*r files. +.SH "SETUID USE" +To prevent anybody but their \*r administrator from deleting revisions, +a set of users can employ setuid privileges as follows. +.nr n \w'\(bu'+2n-1/1n +.ds n \nn +.if \n(.g .if r an-tag-sep .ds n \w'\(bu'u+\n[an-tag-sep]u +.IP \(bu \*n +Check that the host supports \*r setuid use. +Consult a trustworthy expert if there are any doubts. +It is best if the +.B seteuid +system call works as described in Posix 1003.1a Draft 5, +because \*r can switch back and forth easily +between real and effective users, even if the real user is +.BR root . +If not, the second best is if the +.B setuid +system call supports saved setuid +(the {\s-1_POSIX_SAVED_IDS\s0} behavior of Posix 1003.1-1990); +this fails only if the real or effective user is +.BR root . +If \*r detects any failure in setuid, it quits immediately. +.IP \(bu \nn +Choose a user +.I A +to serve as \*r administrator for the set of users. +Only +.I A +can invoke the +.B rcs +command on the users' \*r files. +.I A +should not be +.B root +or any other user with special powers. +Mutually suspicious sets of users should use different administrators. +.IP \(bu \nn +Choose a pathname +.I B +to be a directory of files to be executed by the users. +.IP \(bu \nn +Have +.I A +set up +.I B +to contain copies of +.B ci +and +.B co +that are setuid to +.I A +by copying the commands from their standard installation directory +.I D +as follows: +.LP +.RS +.nf +.ne 3 +\f3mkdir\fP \f2B\fP +\f3cp\fP \f2D\fP\^\f3/c[io]\fP \f2B\fP +\f3chmod go\-w,u+s\fP \f2B\fP\f3/c[io]\fP +.fi +.RE +.IP \(bu \nn +Have each user prepend +.I B +to their path as follows: +.LP +.RS +.nf +.ne 2 +\f3PATH=\fP\f2B\fP\f3:$PATH; export PATH\fP # ordinary shell +\f3set path=(\fP\f2B\fP \f3$path)\fP # C shell +.fi +.RE +.IP \(bu \nn +Have +.I A +create each \*r directory +.I R +with write access only to +.I A +as follows: +.LP +.RS +.nf +.ne 2 +\f3mkdir\fP \f2R\fP +\f3chmod go\-w\fP \f2R\fP +.fi +.RE +.IP \(bu \nn +If you want to let only certain users read the \*r files, +put the users into a group +.IR G , +and have +.I A +further protect the \*r directory as follows: +.LP +.RS +.nf +.ne 2 +\f3chgrp\fP \f2G R\fP +\f3chmod g\-w,o\-rwx\fP \f2R\fP +.fi +.RE +.IP \(bu \nn +Have +.I A +copy old \*r files (if any) into +.IR R , +to ensure that +.I A +owns them. +.IP \(bu \nn +An \*r file's access list limits who can check in and lock revisions. +The default access list is empty, +which grants checkin access to anyone who can read the \*r file. +If you want limit checkin access, +have +.I A +invoke +.B "rcs\ \-a" +on the file; see +.BR rcs (1). +In particular, +.BI "rcs\ \-e\ \-a" A +limits access to just +.IR A . +.IP \(bu \nn +Have +.I A +initialize any new \*r files with +.B "rcs\ \-i" +before initial checkin, adding the +.B \-a +option if you want to limit checkin access. +.IP \(bu \nn +Give setuid privileges only to +.BR ci , +.BR co , +and +.BR rcsclean ; +do not give them to +.B rcs +or to any other command. +.IP \(bu \nn +Do not use other setuid commands to invoke \*r commands; +setuid is trickier than you think! +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +A backslash escapes spaces within an option. +The +.B \s-1RCSINIT\s0 +options are prepended to the argument lists of most \*r commands. +Useful +.B \s-1RCSINIT\s0 +options include +.BR \-q , +.BR \-V , +.BR \-x , +and +.BR \-z . +.TP +.B \s-1TMPDIR\s0 +Name of the temporary directory. +If not set, the environment variables +.B \s-1TMP\s0 +and +.B \s-1TEMP\s0 +are inspected instead and the first value found is taken; +if none of them are set, +a host-dependent default is used, typically +.BR /tmp . +.SH DIAGNOSTICS +For each revision, +.B ci +prints the \*r file, the working file, and the number +of both the deposited and the preceding revision. +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +co(1), +ident(1), make(1), rcs(1), rcsclean(1), rcsdiff(1), +rcsintro(1), rcsmerge(1), rlog(1), setuid(2), rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.br diff --git a/gnu/usr.bin/rcs/ci/ci.c b/gnu/usr.bin/rcs/ci/ci.c new file mode 100644 index 00000000000..749a4cf6dbe --- /dev/null +++ b/gnu/usr.bin/rcs/ci/ci.c @@ -0,0 +1,1318 @@ +/* Check in revisions of RCS files from working files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.30 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.29 1995/06/01 16:23:43 eggert + * (main): Add -kb. + * Use `cmpdate', not `cmpnum', to compare dates. + * This is for MKS RCS's incompatible 20th-century date format. + * Don't worry about errno after ftruncate fails. + * Fix input file rewinding bug when large_memory && !maps_memory + * and checking in a branch tip. + * + * (fixwork): Fall back on chmod if fchmod fails, since it might be ENOSYS. + * + * Revision 5.28 1994/03/20 04:52:58 eggert + * Do not generate a corrupted RCS file if the user modifies the working file + * while `ci' is running. + * Do not remove the lock when `ci -l' reverts. + * Move buffer-flushes out of critical sections, since they aren't critical. + * Use ORCSerror to clean up after a fatal error. + * Specify subprocess input via file descriptor, not file name. + * + * Revision 5.27 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.26 1993/11/03 17:42:27 eggert + * Add -z. Don't subtract from RCS file timestamp even if -T. + * Scan for and use Name keyword if -k. + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.25 1992/07/28 16:12:44 eggert + * Add -i, -j, -V. Check that working and RCS files are distinct. + * + * Revision 5.24 1992/02/17 23:02:06 eggert + * `-rREV' now just specifies a revision REV; only bare `-r' reverts to default. + * Add -T. + * + * Revision 5.23 1992/01/27 16:42:51 eggert + * Always unlock branchpoint if caller has a lock. + * Add support for bad_chmod_close, bad_creat0. lint -> RCS_lint + * + * Revision 5.22 1992/01/06 02:42:34 eggert + * Invoke utime() before chmod() to keep some buggy systems happy. + * + * Revision 5.21 1991/11/20 17:58:07 eggert + * Don't read the delta tree from a nonexistent RCS file. + * + * Revision 5.20 1991/10/07 17:32:46 eggert + * Fix log bugs. Remove lint. + * + * Revision 5.19 1991/09/26 23:10:30 eggert + * Plug file descriptor leak. + * + * Revision 5.18 1991/09/18 07:29:10 eggert + * Work around a common ftruncate() bug. + * + * Revision 5.17 1991/09/10 22:15:46 eggert + * Fix test for redirected stdin. + * + * Revision 5.16 1991/08/19 23:17:54 eggert + * When there are no changes, revert to previous revision instead of aborting. + * Add piece tables, -M, -r$. Tune. + * + * Revision 5.15 1991/04/21 11:58:14 eggert + * Ensure that working file is newer than RCS file after ci -[lu]. + * Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.14 1991/02/28 19:18:47 eggert + * Don't let a setuid ci create a new RCS file; rcs -i -a must be run first. + * Fix ci -ko -l mode bug. Open work file at most once. + * + * Revision 5.13 1991/02/25 07:12:33 eggert + * getdate -> getcurdate (SVR4 name clash) + * + * Revision 5.12 1990/12/31 01:00:12 eggert + * Don't use uninitialized storage when handling -{N,n}. + * + * Revision 5.11 1990/12/04 05:18:36 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.10 1990/11/05 20:30:10 eggert + * Don't remove working file when aborting due to no changes. + * + * Revision 5.9 1990/11/01 05:03:23 eggert + * Add -I and new -t behavior. Permit arbitrary data in logs. + * + * Revision 5.8 1990/10/04 06:30:09 eggert + * Accumulate exit status across files. + * + * Revision 5.7 1990/09/25 20:11:46 hammer + * fixed another small typo + * + * Revision 5.6 1990/09/24 21:48:50 hammer + * added cleanups from Paul Eggert. + * + * Revision 5.5 1990/09/21 06:16:38 hammer + * made it handle multiple -{N,n}'s. Also, made it treat re-directed stdin + * the same as the terminal + * + * Revision 5.4 1990/09/20 02:38:51 eggert + * ci -k now checks dates more thoroughly. + * + * Revision 5.3 1990/09/11 02:41:07 eggert + * Fix revision bug with `ci -k file1 file2'. + * + * Revision 5.2 1990/09/04 08:02:10 eggert + * Permit adjacent revisions with identical time stamps (possible on fast hosts). + * Improve incomplete line handling. Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:13:44 eggert + * Expand locker value like co. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:10:00 eggert + * Don't require a final newline. + * Make lock and temp files faster and safer. + * Remove compile-time limits; use malloc instead. + * Permit dates past 1999/12/31. Switch to GMT. + * Add setuid support. Don't pass +args to diff. Check diff's output. + * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. + * Check diff's output. + * + * Revision 4.9 89/05/01 15:10:54 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.8 88/11/08 13:38:23 narten + * changes from root@seismo.CSS.GOV (Super User) + * -d with no arguments uses the mod time of the file it is checking in + * + * Revision 4.7 88/08/09 19:12:07 eggert + * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one. + * Use execv(), not system(); allow cc -R; remove lint. + * isatty(fileno(stdin)) -> ttystdin() + * + * Revision 4.6 87/12/18 11:34:41 narten + * lint cleanups (from Guy Harris) + * + * Revision 4.5 87/10/18 10:18:48 narten + * Updating version numbers. Changes relative to revision 1.1 are actually + * relative to 4.3 + * + * Revision 1.3 87/09/24 13:57:19 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:21:33 jenkins + * Port to suns + * + * Revision 4.3 83/12/15 12:28:54 wft + * ci -u and ci -l now set mode of working file properly. + * + * Revision 4.2 83/12/05 13:40:54 wft + * Merged with 3.9.1.1: added calls to clearerr(stdin). + * made rewriteflag external. + * + * Revision 4.1 83/05/10 17:03:06 wft + * Added option -d and -w, and updated assingment of date, etc. to new delta. + * Added handling of default branches. + * Option -k generates std. log message; fixed undef. pointer in reading of log. + * Replaced getlock() with findlock(), link--unlink with rename(), + * getpwuid() with getcaller(). + * Moved all revision number generation to new routine addelta(). + * Removed calls to stat(); now done by pairfilenames(). + * Changed most calls to catchints() with restoreints(). + * Directed all interactive messages to stderr. + * + * Revision 3.9.1.1 83/10/19 04:21:03 lepreau + * Added clearerr(stdin) to getlogmsg() for re-reading stdin. + * + * Revision 3.9 83/02/15 15:25:44 wft + * 4.2 prerelease + * + * Revision 3.9 83/02/15 15:25:44 wft + * Added call to fastcopy() to copy remainder of RCS file. + * + * Revision 3.8 83/01/14 15:34:05 wft + * Added ignoring of interrupts while new RCS file is renamed; + * Avoids deletion of RCS files by interrupts. + * + * Revision 3.7 82/12/10 16:09:20 wft + * Corrected checking of return code from diff. + * + * Revision 3.6 82/12/08 21:34:49 wft + * Using DATEFORM to prepare date of checked-in revision; + * Fixed return from addbranch(). + * + * Revision 3.5 82/12/04 18:32:42 wft + * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated + * field lockedby in removelock(), moved getlogmsg() before calling diff. + * + * Revision 3.4 82/12/02 13:27:13 wft + * added option -k. + * + * Revision 3.3 82/11/28 20:53:31 wft + * Added mustcheckin() to check for redundant checkins. + * Added xpandfile() to do keyword expansion for -u and -l; + * -m appends linefeed to log message if necessary. + * getlogmsg() suppresses prompt if stdin is not a terminal. + * Replaced keeplock with lockflag, fclose() with ffclose(), + * %02d with %.2d, getlogin() with getpwuid(). + * + * Revision 3.2 82/10/18 20:57:23 wft + * An RCS file inherits its mode during the first ci from the working file, + * otherwise it stays the same, except that write permission is removed. + * Fixed ci -l, added ci -u (both do an implicit co after the ci). + * Fixed call to getlogin(), added call to getfullRCSname(), added check + * for write error. + * Changed conflicting identifiers. + * + * Revision 3.1 82/10/13 16:04:59 wft + * fixed type of variables receiving from getc() (char -> int). + * added include file dbm.h for getting BYTESIZ. This is used + * to check the return code from diff portably. + */ + +#include "rcsbase.h" + +struct Symrev { + char const *ssymbol; + int override; + struct Symrev * nextsym; +}; + +static char const *getcurdate P((void)); +static int addbranch P((struct hshentry*,struct buf*,int)); +static int addelta P((void)); +static int addsyms P((char const*)); +static int fixwork P((mode_t,time_t)); +static int removelock P((struct hshentry*)); +static int xpandfile P((RILE*,struct hshentry const*,char const**,int)); +static struct cbuf getlogmsg P((void)); +static void cleanup P((void)); +static void incnum P((char const*,struct buf*)); +static void addassoclst P((int,char const*)); + +static FILE *exfile; +static RILE *workptr; /* working file pointer */ +static struct buf newdelnum; /* new revision number */ +static struct cbuf msg; +static int exitstatus; +static int forceciflag; /* forces check in */ +static int keepflag, keepworkingfile, rcsinitflag; +static struct hshentries *gendeltas; /* deltas to be generated */ +static struct hshentry *targetdelta; /* old delta to be generated */ +static struct hshentry newdelta; /* new delta to be inserted */ +static struct stat workstat; +static struct Symrev *assoclst, **nextassoc; + +mainProg(ciId, "ci", "$FreeBSD$") +{ + static char const cmdusage[] = + "\nci usage: ci -{fIklMqru}[rev] -d[date] -mmsg -{nN}name -sstate -ttext -T -Vn -wwho -xsuff -zzone file ..."; + static char const default_state[] = DEFAULTSTATE; + + char altdate[datesize]; + char olddate[datesize]; + char newdatebuf[datesize + zonelenmax]; + char targetdatebuf[datesize + zonelenmax]; + char *a, **newargv, *textfile; + char const *author, *krev, *rev, *state; + char const *diffname, *expname; + char const *newworkname; + int initflag, mustread; + int lockflag, lockthis, mtimeflag, removedlock, Ttimeflag; + int r; + int changedRCS, changework, dolog, newhead; + int usestatdate; /* Use mod time of file for -d. */ + mode_t newworkmode; /* mode for working file */ + time_t mtime, wtime; + struct hshentry *workdelta; + + setrid(); + + author = rev = state = textfile = 0; + initflag = lockflag = mustread = false; + mtimeflag = false; + Ttimeflag = false; + altdate[0]= '\0'; /* empty alternate date for -d */ + usestatdate=false; + suffixes = X_DEFAULT; + nextassoc = &assoclst; + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + while (a = *++argv, 0<--argc && *a++=='-') { + switch (*a++) { + + case 'r': + if (*a) + goto revno; + keepworkingfile = lockflag = false; + break; + + case 'l': + keepworkingfile = lockflag = true; + revno: + if (*a) { + if (rev) warn("redefinition of revision number"); + rev = a; + } + break; + + case 'u': + keepworkingfile=true; lockflag=false; + goto revno; + + case 'i': + initflag = true; + goto revno; + + case 'j': + mustread = true; + goto revno; + + case 'I': + interactiveflag = true; + goto revno; + + case 'q': + quietflag=true; + goto revno; + + case 'f': + forceciflag=true; + goto revno; + + case 'k': + keepflag=true; + goto revno; + + case 'm': + if (msg.size) redefined('m'); + msg = cleanlogmsg(a, strlen(a)); + if (!msg.size) + error("missing message for -m option"); + break; + + case 'n': + if (!*a) { + error("missing symbolic name after -n"); + break; + } + checkssym(a); + addassoclst(false, a); + break; + + case 'N': + if (!*a) { + error("missing symbolic name after -N"); + break; + } + checkssym(a); + addassoclst(true, a); + break; + + case 's': + if (*a) { + if (state) redefined('s'); + checksid(a); + state = a; + } else + error("missing state for -s option"); + break; + + case 't': + if (*a) { + if (textfile) redefined('t'); + textfile = a; + } + break; + + case 'd': + if (altdate[0] || usestatdate) + redefined('d'); + altdate[0] = '\0'; + if (!(usestatdate = !*a)) + str2date(a, altdate); + break; + + case 'M': + mtimeflag = true; + goto revno; + + case 'w': + if (*a) { + if (author) redefined('w'); + checksid(a); + author = a; + } else + error("missing author for -w option"); + break; + + case 'x': + suffixes = a; + break; + + case 'V': + setRCSversion(*argv); + break; + + case 'z': + zone_set(a); + break; + + case 'T': + if (!*a) { + Ttimeflag = true; + break; + } + /* fall into */ + default: + error("unknown option: %s%s", *argv, cmdusage); + }; + } /* end processing of options */ + + /* Handle all pathnames. */ + if (nerror) cleanup(); + else if (argc < 1) faterror("no input file%s", cmdusage); + else for (; 0 < argc; cleanup(), ++argv, --argc) { + targetdelta = 0; + ffree(); + + switch (pairnames(argc, argv, rcswriteopen, mustread, false)) { + + case -1: /* New RCS file */ +# if has_setuid && has_getuid + if (euid() != ruid()) { + workerror("setuid initial checkin prohibited; use `rcs -i -a' first"); + continue; + } +# endif + rcsinitflag = true; + break; + + case 0: /* Error */ + continue; + + case 1: /* Normal checkin with prev . RCS file */ + if (initflag) { + rcserror("already exists"); + continue; + } + rcsinitflag = !Head; + } + + /* + * RCSname contains the name of the RCS file, and + * workname contains the name of the working file. + * If the RCS file exists, finptr contains the file descriptor for the + * RCS file, and RCSstat is set. The admin node is initialized. + */ + + diagnose("%s <-- %s\n", RCSname, workname); + + if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) { + eerror(workname); + continue; + } + + if (finptr) { + if (same_file(RCSstat, workstat, 0)) { + rcserror("RCS file is the same as working file %s.", + workname + ); + continue; + } + if (!checkaccesslist()) + continue; + } + + krev = rev; + if (keepflag) { + /* get keyword values from working file */ + if (!getoldkeys(workptr)) continue; + if (!rev && !*(krev = prevrev.string)) { + workerror("can't find a revision number"); + continue; + } + if (!*prevdate.string && *altdate=='\0' && usestatdate==false) + workwarn("can't find a date"); + if (!*prevauthor.string && !author) + workwarn("can't find an author"); + if (!*prevstate.string && !state) + workwarn("can't find a state"); + } /* end processing keepflag */ + + /* Read the delta tree. */ + if (finptr) + gettree(); + + /* expand symbolic revision number */ + if (!fexpandsym(krev, &newdelnum, workptr)) + continue; + + /* splice new delta into tree */ + if ((removedlock = addelta()) < 0) + continue; + + newdelta.num = newdelnum.string; + newdelta.branches = 0; + newdelta.lockedby = 0; /* This might be changed by addlock(). */ + newdelta.selector = true; + newdelta.name = 0; + clear_buf(&newdelta.ig); + clear_buf(&newdelta.igtext); + /* set author */ + if (author) + newdelta.author=author; /* set author given by -w */ + else if (keepflag && *prevauthor.string) + newdelta.author=prevauthor.string; /* preserve old author if possible*/ + else newdelta.author=getcaller();/* otherwise use caller's id */ + newdelta.state = default_state; + if (state) + newdelta.state=state; /* set state given by -s */ + else if (keepflag && *prevstate.string) + newdelta.state=prevstate.string; /* preserve old state if possible */ + if (usestatdate) { + time2date(workstat.st_mtime, altdate); + } + if (*altdate!='\0') + newdelta.date=altdate; /* set date given by -d */ + else if (keepflag && *prevdate.string) { + /* Preserve old date if possible. */ + str2date(prevdate.string, olddate); + newdelta.date = olddate; + } else + newdelta.date = getcurdate(); /* use current date */ + /* now check validity of date -- needed because of -d and -k */ + if (targetdelta && + cmpdate(newdelta.date,targetdelta->date) < 0) { + rcserror("Date %s precedes %s in revision %s.", + date2str(newdelta.date, newdatebuf), + date2str(targetdelta->date, targetdatebuf), + targetdelta->num + ); + continue; + } + + + if (lockflag && addlock(&newdelta, true) < 0) continue; + + if (keepflag && *prevname.string) + if (addsymbol(newdelta.num, prevname.string, false) < 0) + continue; + if (!addsyms(newdelta.num)) + continue; + + + putadmin(); + puttree(Head,frewrite); + putdesc(false,textfile); + + changework = Expand < MIN_UNCHANGED_EXPAND; + dolog = true; + lockthis = lockflag; + workdelta = &newdelta; + + /* build rest of file */ + if (rcsinitflag) { + diagnose("initial revision: %s\n", newdelta.num); + /* get logmessage */ + newdelta.log=getlogmsg(); + putdftext(&newdelta, workptr, frewrite, false); + RCSstat.st_mode = workstat.st_mode; + RCSstat.st_nlink = 0; + changedRCS = true; + } else { + diffname = maketemp(0); + newhead = Head == &newdelta; + if (!newhead) + foutptr = frewrite; + expname = buildrevision( + gendeltas, targetdelta, (FILE*)0, false + ); + if ( + !forceciflag && + strcmp(newdelta.state, targetdelta->state) == 0 && + (changework = rcsfcmp( + workptr, &workstat, expname, targetdelta + )) <= 0 + ) { + diagnose("file is unchanged; reverting to previous revision %s\n", + targetdelta->num + ); + if (removedlock < lockflag) { + diagnose("previous revision was not locked; ignoring -l option\n"); + lockthis = 0; + } + dolog = false; + if (! (changedRCS = lockflagnum, (char*)0, (char*)0, (char*)0, + &gendeltas + ))) + continue; + workdelta->log = targetdelta->log; + if (newdelta.state != default_state) + workdelta->state = newdelta.state; + if (lockthisnum)) + continue; + if (dorewrite(true, true) != 0) + continue; + fastcopy(finptr, frewrite); + if (bad_truncate) + while (ftell(frewrite) < hwm) + /* White out any earlier mistake with '\n's. */ + /* This is unlikely. */ + afputc('\n', frewrite); + } + } else { + int wfd = Ifileno(workptr); + struct stat checkworkstat; + char const *diffv[6 + !!OPEN_O_BINARY], **diffp; +# if large_memory && !maps_memory + FILE *wfile = workptr->stream; + long wfile_off; +# endif +# if !has_fflush_input && !(large_memory && maps_memory) + off_t wfd_off; +# endif + + diagnose("new revision: %s; previous revision: %s\n", + newdelta.num, targetdelta->num + ); + newdelta.log = getlogmsg(); +# if !large_memory + Irewind(workptr); +# if has_fflush_input + if (fflush(workptr) != 0) + Ierror(); +# endif +# else +# if !maps_memory + if ( + (wfile_off = ftell(wfile)) == -1 + || fseek(wfile, 0L, SEEK_SET) != 0 +# if has_fflush_input + || fflush(wfile) != 0 +# endif + ) + Ierror(); +# endif +# endif +# if !has_fflush_input && !(large_memory && maps_memory) + wfd_off = lseek(wfd, (off_t)0, SEEK_CUR); + if (wfd_off == -1 + || (wfd_off != 0 + && lseek(wfd, (off_t)0, SEEK_SET) != 0)) + Ierror(); +# endif + diffp = diffv; + *++diffp = DIFF; + *++diffp = DIFFFLAGS; +# if OPEN_O_BINARY + if (Expand == BINARY_EXPAND) + *++diffp = "--binary"; +# endif + *++diffp = newhead ? "-" : expname; + *++diffp = newhead ? expname : "-"; + *++diffp = 0; + switch (runv(wfd, diffname, diffv)) { + case DIFF_FAILURE: case DIFF_SUCCESS: break; + default: rcsfaterror("diff failed"); + } +# if !has_fflush_input && !(large_memory && maps_memory) + if (lseek(wfd, wfd_off, SEEK_CUR) == -1) + Ierror(); +# endif +# if large_memory && !maps_memory + if (fseek(wfile, wfile_off, SEEK_SET) != 0) + Ierror(); +# endif + if (newhead) { + Irewind(workptr); + putdftext(&newdelta, workptr, frewrite, false); + if (!putdtext(targetdelta,diffname,frewrite,true)) continue; + } else + if (!putdtext(&newdelta,diffname,frewrite,true)) continue; + + /* + * Check whether the working file changed during checkin, + * to avoid producing an inconsistent RCS file. + */ + if ( + fstat(wfd, &checkworkstat) != 0 + || workstat.st_mtime != checkworkstat.st_mtime + || workstat.st_size != checkworkstat.st_size + ) { + workerror("file changed during checkin"); + continue; + } + + changedRCS = true; + } + } + + /* Deduce time_t of new revision if it is needed later. */ + wtime = (time_t)-1; + if (mtimeflag | Ttimeflag) + wtime = date2time(workdelta->date); + + if (donerewrite(changedRCS, + !Ttimeflag ? (time_t)-1 + : finptr && wtime < RCSstat.st_mtime ? RCSstat.st_mtime + : wtime + ) != 0) + continue; + + if (!keepworkingfile) { + Izclose(&workptr); + r = un_link(workname); /* Get rid of old file */ + } else { + newworkmode = WORKMODE(RCSstat.st_mode, + ! (Expand==VAL_EXPAND || lockthis < StrictLocks) + ); + mtime = mtimeflag ? wtime : (time_t)-1; + + /* Expand if it might change or if we can't fix mode, time. */ + if (changework || (r=fixwork(newworkmode,mtime)) != 0) { + Irewind(workptr); + /* Expand keywords in file. */ + locker_expansion = lockthis; + workdelta->name = + namedrev( + assoclst ? assoclst->ssymbol + : keepflag && *prevname.string ? prevname.string + : rev, + workdelta + ); + switch (xpandfile( + workptr, workdelta, &newworkname, dolog + )) { + default: + continue; + + case 0: + /* + * No expansion occurred; try to reuse working file + * unless we already tried and failed. + */ + if (changework) + if ((r=fixwork(newworkmode,mtime)) == 0) + break; + /* fall into */ + case 1: + Izclose(&workptr); + aflush(exfile); + ignoreints(); + r = chnamemod(&exfile, newworkname, + workname, 1, newworkmode, mtime + ); + keepdirtemp(newworkname); + restoreints(); + } + } + } + if (r != 0) { + eerror(workname); + continue; + } + diagnose("done\n"); + + } + + tempunlink(); + exitmain(exitstatus); +} /* end of main (ci) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); + Izclose(&workptr); + Ozclose(&exfile); + Ozclose(&fcopy); + ORCSclose(); + dirtempunlink(); +} + +#if RCS_lint +# define exiterr ciExit +#endif + void +exiterr() +{ + ORCSerror(); + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + +/*****************************************************************/ +/* the rest are auxiliary routines */ + + + static int +addelta() +/* Function: Appends a delta to the delta tree, whose number is + * given by newdelnum. Updates Head, newdelnum, newdelnumlength, + * and the links in newdelta. + * Return -1 on error, 1 if a lock is removed, 0 otherwise. + */ +{ + register char *tp; + register int i; + int removedlock; + int newdnumlength; /* actual length of new rev. num. */ + + newdnumlength = countnumflds(newdelnum.string); + + if (rcsinitflag) { + /* this covers non-existing RCS file and a file initialized with rcs -i */ + if (newdnumlength==0 && Dbranch) { + bufscpy(&newdelnum, Dbranch); + newdnumlength = countnumflds(Dbranch); + } + if (newdnumlength==0) bufscpy(&newdelnum, "1.1"); + else if (newdnumlength==1) bufscat(&newdelnum, ".1"); + else if (newdnumlength>2) { + rcserror("Branch point doesn't exist for revision %s.", + newdelnum.string + ); + return -1; + } /* newdnumlength == 2 is OK; */ + Head = &newdelta; + newdelta.next = 0; + return 0; + } + if (newdnumlength==0) { + /* derive new revision number from locks */ + switch (findlock(true, &targetdelta)) { + + default: + /* found two or more old locks */ + return -1; + + case 1: + /* found an old lock */ + /* check whether locked revision exists */ + if (!genrevs(targetdelta->num,(char*)0,(char*)0,(char*)0,&gendeltas)) + return -1; + if (targetdelta==Head) { + /* make new head */ + newdelta.next=Head; + Head= &newdelta; + } else if (!targetdelta->next && countnumflds(targetdelta->num)>2) { + /* new tip revision on side branch */ + targetdelta->next= &newdelta; + newdelta.next = 0; + } else { + /* middle revision; start a new branch */ + bufscpy(&newdelnum, ""); + return addbranch(targetdelta, &newdelnum, 1); + } + incnum(targetdelta->num, &newdelnum); + return 1; /* successful use of existing lock */ + + case 0: + /* no existing lock; try Dbranch */ + /* update newdelnum */ + if (StrictLocks || !myself(RCSstat.st_uid)) { + rcserror("no lock set by %s", getcaller()); + return -1; + } + if (Dbranch) { + bufscpy(&newdelnum, Dbranch); + } else { + incnum(Head->num, &newdelnum); + } + newdnumlength = countnumflds(newdelnum.string); + /* now fall into next statement */ + } + } + if (newdnumlength<=2) { + /* add new head per given number */ + if(newdnumlength==1) { + /* make a two-field number out of it*/ + if (cmpnumfld(newdelnum.string,Head->num,1)==0) + incnum(Head->num, &newdelnum); + else + bufscat(&newdelnum, ".1"); + } + if (cmpnum(newdelnum.string,Head->num) <= 0) { + rcserror("revision %s too low; must be higher than %s", + newdelnum.string, Head->num + ); + return -1; + } + targetdelta = Head; + if (0 <= (removedlock = removelock(Head))) { + if (!genrevs(Head->num,(char*)0,(char*)0,(char*)0,&gendeltas)) + return -1; + newdelta.next = Head; + Head = &newdelta; + } + return removedlock; + } else { + /* put new revision on side branch */ + /*first, get branch point */ + tp = newdelnum.string; + for (i = newdnumlength - ((newdnumlength&1) ^ 1); --i; ) + while (*tp++ != '.') + continue; + *--tp = 0; /* Kill final dot to get old delta temporarily. */ + if (!(targetdelta=genrevs(newdelnum.string,(char*)0,(char*)0,(char*)0,&gendeltas))) + return -1; + if (cmpnum(targetdelta->num, newdelnum.string) != 0) { + rcserror("can't find branch point %s", newdelnum.string); + return -1; + } + *tp = '.'; /* Restore final dot. */ + return addbranch(targetdelta, &newdelnum, 0); + } +} + + + + static int +addbranch(branchpoint, num, removedlock) + struct hshentry *branchpoint; + struct buf *num; + int removedlock; +/* adds a new branch and branch delta at branchpoint. + * If num is the null string, appends the new branch, incrementing + * the highest branch number (initially 1), and setting the level number to 1. + * the new delta and branchhead are in globals newdelta and newbranch, resp. + * the new number is placed into num. + * Return -1 on error, 1 if a lock is removed, 0 otherwise. + * If REMOVEDLOCK is 1, a lock was already removed. + */ +{ + struct branchhead *bhead, **btrail; + struct buf branchnum; + int result; + int field, numlength; + static struct branchhead newbranch; /* new branch to be inserted */ + + numlength = countnumflds(num->string); + + if (!branchpoint->branches) { + /* start first branch */ + branchpoint->branches = &newbranch; + if (numlength==0) { + bufscpy(num, branchpoint->num); + bufscat(num, ".1.1"); + } else if (numlength&1) + bufscat(num, ".1"); + newbranch.nextbranch = 0; + + } else if (numlength==0) { + /* append new branch to the end */ + bhead=branchpoint->branches; + while (bhead->nextbranch) bhead=bhead->nextbranch; + bhead->nextbranch = &newbranch; + bufautobegin(&branchnum); + getbranchno(bhead->hsh->num, &branchnum); + incnum(branchnum.string, num); + bufautoend(&branchnum); + bufscat(num, ".1"); + newbranch.nextbranch = 0; + } else { + /* place the branch properly */ + field = numlength - ((numlength&1) ^ 1); + /* field of branch number */ + btrail = &branchpoint->branches; + while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) { + btrail = &(*btrail)->nextbranch; + if (!*btrail) { + result = -1; + break; + } + } + if (result < 0) { + /* insert/append new branchhead */ + newbranch.nextbranch = *btrail; + *btrail = &newbranch; + if (numlength&1) bufscat(num, ".1"); + } else { + /* branch exists; append to end */ + bufautobegin(&branchnum); + getbranchno(num->string, &branchnum); + targetdelta = genrevs( + branchnum.string, (char*)0, (char*)0, (char*)0, + &gendeltas + ); + bufautoend(&branchnum); + if (!targetdelta) + return -1; + if (cmpnum(num->string,targetdelta->num) <= 0) { + rcserror("revision %s too low; must be higher than %s", + num->string, targetdelta->num + ); + return -1; + } + if (!removedlock + && 0 <= (removedlock = removelock(targetdelta)) + ) { + if (numlength&1) + incnum(targetdelta->num,num); + targetdelta->next = &newdelta; + newdelta.next = 0; + } + return removedlock; + /* Don't do anything to newbranch. */ + } + } + newbranch.hsh = &newdelta; + newdelta.next = 0; + if (branchpoint->lockedby) + if (strcmp(branchpoint->lockedby, getcaller()) == 0) + return removelock(branchpoint); /* This returns 1. */ + return removedlock; +} + + static int +addsyms(num) + char const *num; +{ + register struct Symrev *p; + + for (p = assoclst; p; p = p->nextsym) + if (addsymbol(num, p->ssymbol, p->override) < 0) + return false; + return true; +} + + + static void +incnum(onum,nnum) + char const *onum; + struct buf *nnum; +/* Increment the last field of revision number onum by one and + * place the result into nnum. + */ +{ + register char *tp, *np; + register size_t l; + + l = strlen(onum); + bufalloc(nnum, l+2); + np = tp = nnum->string; + VOID strcpy(np, onum); + for (tp = np + l; np != tp; ) + if (isdigit(*--tp)) { + if (*tp != '9') { + ++*tp; + return; + } + *tp = '0'; + } else { + tp++; + break; + } + /* We changed 999 to 000; now change it to 1000. */ + *tp = '1'; + tp = np + l; + *tp++ = '0'; + *tp = 0; +} + + + + static int +removelock(delta) +struct hshentry * delta; +/* function: Finds the lock held by caller on delta, + * removes it, and returns nonzero if successful. + * Print an error message and return -1 if there is no such lock. + * An exception is if !StrictLocks, and caller is the owner of + * the RCS file. If caller does not have a lock in this case, + * return 0; return 1 if a lock is actually removed. + */ +{ + register struct rcslock *next, **trail; + char const *num; + + num=delta->num; + for (trail = &Locks; (next = *trail); trail = &next->nextlock) + if (next->delta == delta) + if (strcmp(getcaller(), next->login) == 0) { + /* We found a lock on delta by caller; delete it. */ + *trail = next->nextlock; + delta->lockedby = 0; + return 1; + } else { + rcserror("revision %s locked by %s", num, next->login); + return -1; + } + if (!StrictLocks && myself(RCSstat.st_uid)) + return 0; + rcserror("no lock set by %s for revision %s", getcaller(), num); + return -1; +} + + + + static char const * +getcurdate() +/* Return a pointer to the current date. */ +{ + static char buffer[datesize]; /* date buffer */ + + if (!buffer[0]) + time2date(now(), buffer); + return buffer; +} + + static int +#if has_prototypes +fixwork(mode_t newworkmode, time_t mtime) + /* The `#if has_prototypes' is needed because mode_t might promote to int. */ +#else + fixwork(newworkmode, mtime) + mode_t newworkmode; + time_t mtime; +#endif +{ + return + 1 < workstat.st_nlink + || (newworkmode&S_IWUSR && !myself(workstat.st_uid)) + || setmtime(workname, mtime) != 0 + ? -1 + : workstat.st_mode == newworkmode ? 0 +#if has_fchmod + : fchmod(Ifileno(workptr), newworkmode) == 0 ? 0 +#endif +#if bad_chmod_close + : -1 +#else + : chmod(workname, newworkmode) +#endif + ; +} + + static int +xpandfile(unexfile, delta, exname, dolog) + RILE *unexfile; + struct hshentry const *delta; + char const **exname; + int dolog; +/* + * Read unexfile and copy it to a + * file, performing keyword substitution with data from delta. + * Return -1 if unsuccessful, 1 if expansion occurred, 0 otherwise. + * If successful, stores the stream descriptor into *EXFILEP + * and its name into *EXNAME. + */ +{ + char const *targetname; + int e, r; + + targetname = makedirtemp(1); + if (!(exfile = fopenSafer(targetname, FOPEN_W_WORK))) { + eerror(targetname); + workerror("can't build working file"); + return -1; + } + r = 0; + if (MIN_UNEXPAND <= Expand) + fastcopy(unexfile,exfile); + else { + for (;;) { + e = expandline( + unexfile, exfile, delta, false, (FILE*)0, dolog + ); + if (e < 0) + break; + r |= e; + if (e <= 1) + break; + } + } + *exname = targetname; + return r & 1; +} + + + + +/* --------------------- G E T L O G M S G --------------------------------*/ + + + static struct cbuf +getlogmsg() +/* Obtain and yield a log message. + * If a log message is given with -m, yield that message. + * If this is the initial revision, yield a standard log message. + * Otherwise, reads a character string from the terminal. + * Stops after reading EOF or a single '.' on a + * line. getlogmsg prompts the first time it is called for the + * log message; during all later calls it asks whether the previous + * log message can be reused. + */ +{ + static char const + emptych[] = EMPTYLOG, + initialch[] = "Initial revision"; + static struct cbuf const + emptylog = { emptych, sizeof(emptych)-sizeof(char) }, + initiallog = { initialch, sizeof(initialch)-sizeof(char) }; + static struct buf logbuf; + static struct cbuf logmsg; + + register char *tp; + register size_t i; + char const *caller; + + if (msg.size) return msg; + + if (keepflag) { + /* generate std. log message */ + caller = getcaller(); + i = sizeof(ciklog)+strlen(caller)+3; + bufalloc(&logbuf, i + datesize + zonelenmax); + tp = logbuf.string; + VOID sprintf(tp, "%s%s at ", ciklog, caller); + VOID date2str(getcurdate(), tp+i); + logmsg.string = tp; + logmsg.size = strlen(tp); + return logmsg; + } + + if (!targetdelta && ( + cmpnum(newdelnum.string,"1.1")==0 || + cmpnum(newdelnum.string,"1.0")==0 + )) + return initiallog; + + if (logmsg.size) { + /*previous log available*/ + if (yesorno(true, "reuse log message of previous file? [yn](y): ")) + return logmsg; + } + + /* now read string from stdin */ + logmsg = getsstdin("m", "log message", "", &logbuf); + + /* now check whether the log message is not empty */ + if (logmsg.size) + return logmsg; + return emptylog; +} + +/* Make a linked list of Symbolic names */ + + static void +addassoclst(flag, sp) + int flag; + char const *sp; +{ + struct Symrev *pt; + + pt = talloc(struct Symrev); + pt->ssymbol = sp; + pt->override = flag; + pt->nextsym = 0; + *nextassoc = pt; + nextassoc = &pt->nextsym; +} diff --git a/gnu/usr.bin/rcs/co/Makefile b/gnu/usr.bin/rcs/co/Makefile new file mode 100644 index 00000000000..0c73865d09f --- /dev/null +++ b/gnu/usr.bin/rcs/co/Makefile @@ -0,0 +1,8 @@ +PROG= co +SRCS= co.c +CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/co/co.1 b/gnu/usr.bin/rcs/co/co.1 new file mode 100644 index 00000000000..2aea3ad1b40 --- /dev/null +++ b/gnu/usr.bin/rcs/co/co.1 @@ -0,0 +1,736 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds i \&\s-1ISO\s0 +.ds r \&\s-1RCS\s0 +.ds u \&\s-1UTC\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH CO 1 \*(Dt GNU +.SH NAME +co \- check out RCS revisions +.SH SYNOPSIS +.B co +.RI [ options ] " file " .\|.\|. +.SH DESCRIPTION +.B co +retrieves a revision from each \*r file and stores it into +the corresponding working file. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +.PP +Revisions of an \*r file can be checked out locked or unlocked. Locking a +revision prevents overlapping updates. A revision checked out for reading or +processing (e.g., compiling) need not be locked. A revision checked out +for editing and later checkin must normally be locked. Checkout with locking +fails if the revision to be checked out is currently locked by another user. +(A lock can be broken with +.BR rcs "(1).)\ \&" +Checkout with locking also requires the caller to be on the access list of +the \*r file, unless he is the owner of the +file or the superuser, or the access list is empty. +Checkout without locking is not subject to accesslist restrictions, and is +not affected by the presence of locks. +.PP +A revision is selected by options for revision or branch number, +checkin date/time, author, or state. +When the selection options +are applied in combination, +.B co +retrieves the latest revision +that satisfies all of them. +If none of the selection options +is specified, +.B co +retrieves the latest revision +on the default branch (normally the trunk, see the +.B \-b +option of +.BR rcs (1)). +A revision or branch number can be attached +to any of the options +.BR \-f , +.BR \-I , +.BR \-l , +.BR \-M , +.BR \-p , +.BR \-q , +.BR \-r , +or +.BR \-u . +The options +.B \-d +(date), +.B \-s +(state), and +.B \-w +(author) +retrieve from a single branch, the +.I selected +branch, +which is either specified by one of +.BR \-f , +\&.\|.\|., +.BR \-u , +or the default branch. +.PP +A +.B co +command applied to an \*r +file with no revisions creates a zero-length working file. +.B co +always performs keyword substitution (see below). +.SH OPTIONS +.TP +.BR \-r [\f2rev\fP] +retrieves the latest revision whose number is less than or equal to +.IR rev . +If +.I rev +indicates a branch rather than a revision, +the latest revision on that branch is retrieved. +If +.I rev +is omitted, the latest revision on the default branch +(see the +.B \-b +option of +.BR rcs (1)) +is retrieved. +If +.I rev +is +.BR $ , +.B co +determines the revision number from keyword values in the working file. +Otherwise, a revision is composed of one or more numeric or symbolic fields +separated by periods. +If +.I rev +begins with a period, +then the default branch (normally the trunk) is prepended to it. +If +.I rev +is a branch number followed by a period, +then the latest revision on that branch is used. +The numeric equivalent of a symbolic field +is specified with the +.B \-n +option of the commands +.BR ci (1) +and +.BR rcs (1). +.TP +.BR \-l [\f2rev\fP] +same as +.BR \-r , +except that it also locks the retrieved revision for +the caller. +.TP +.BR \-u [\f2rev\fP] +same as +.BR \-r , +except that it unlocks the retrieved revision if it was +locked by the caller. If +.I rev +is omitted, +.B \-u +retrieves the revision locked by the caller, if there is one; otherwise, +it retrieves the latest revision on the default branch. +.TP +.BR \-f [\f2rev\fP] +forces the overwriting of the working file; +useful in connection with +.BR \-q . +See also +.SM "FILE MODES" +below. +.TP +.B \-kkv +Generate keyword strings using the default form, e.g.\& +.B "$\&Revision: \*(Rv $" +for the +.B Revision +keyword. +A locker's name is inserted in the value of the +.BR Header , +.BR Id , +and +.B Locker +keyword strings +only as a file is being locked, +i.e. by +.B "ci\ \-l" +and +.BR "co\ \-l". +This is the default. +.TP +.B \-kkvl +Like +.BR \-kkv , +except that a locker's name is always inserted +if the given revision is currently locked. +.TP +.B \-kk +Generate only keyword names in keyword strings; omit their values. +See +.SM "KEYWORD SUBSTITUTION" +below. +For example, for the +.B Revision +keyword, generate the string +.B $\&Revision$ +instead of +.BR "$\&Revision: \*(Rv $" . +This option is useful to ignore differences due to keyword substitution +when comparing different revisions of a file. +Log messages are inserted after +.B $\&Log$ +keywords even if +.B \-kk +is specified, +since this tends to be more useful when merging changes. +.TP +.B \-ko +Generate the old keyword string, +present in the working file just before it was checked in. +For example, for the +.B Revision +keyword, generate the string +.B "$\&Revision: 1.1 $" +instead of +.B "$\&Revision: \*(Rv $" +if that is how the string appeared when the file was checked in. +This can be useful for file formats +that cannot tolerate any changes to substrings +that happen to take the form of keyword strings. +.TP +.B \-kb +Generate a binary image of the old keyword string. +This acts like +.BR \-ko , +except it performs all working file input and output in binary mode. +This makes little difference on Posix and Unix hosts, +but on DOS-like hosts one should use +.B "rcs\ \-i\ \-kb" +to initialize an \*r file intended to be used for binary files. +Also, on all hosts, +.BR rcsmerge (1) +normally refuses to merge files when +.B \-kb +is in effect. +.TP +.B \-kv +Generate only keyword values for keyword strings. +For example, for the +.B Revision +keyword, generate the string +.B \*(Rv +instead of +.BR "$\&Revision: \*(Rv $" . +This can help generate files in programming languages where it is hard to +strip keyword delimiters like +.B "$\&Revision:\ $" +from a string. +However, further keyword substitution cannot be performed once the +keyword names are removed, so this option should be used with care. +Because of this danger of losing keywords, +this option cannot be combined with +.BR \-l , +and the owner write permission of the working file is turned off; +to edit the file later, check it out again without +.BR \-kv . +.TP +.BR \-p [\f2rev\fP] +prints the retrieved revision on the standard output rather than storing it +in the working file. +This option is useful when +.B co +is part of a pipe. +.TP +.BR \-q [\f2rev\fP] +quiet mode; diagnostics are not printed. +.TP +.BR \-I [\f2rev\fP] +interactive mode; +the user is prompted and questioned +even if the standard input is not a terminal. +.TP +.BI \-d date +retrieves the latest revision on the selected branch whose checkin date/time is +less than or equal to +.IR date . +The date and time can be given in free format. +The time zone +.B LT +stands for local time; +other common time zone names are understood. +For example, the following +.IR date s +are equivalent +if local time is January 11, 1990, 8pm Pacific Standard Time, +eight hours west of Coordinated Universal Time (\*u): +.RS +.LP +.RS +.nf +.ta \w'\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP 'u +.ne 10 +\f38:00 pm lt\fP +\f34:00 AM, Jan. 12, 1990\fP default is \*u +\f31990-01-12 04:00:00+00\fP \*i 8601 (\*u) +\f31990-01-11 20:00:00\-08\fP \*i 8601 (local time) +\f31990/01/12 04:00:00\fP traditional \*r format +\f3Thu Jan 11 20:00:00 1990 LT\fP output of \f3ctime\fP(3) + \f3LT\fP +\f3Thu Jan 11 20:00:00 PST 1990\fP output of \f3date\fP(1) +\f3Fri Jan 12 04:00:00 GMT 1990\fP +\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP Internet RFC 822 +\f312-January-1990, 04:00 WET\fP +.ta 4n +4n +4n +4n +.fi +.RE +.LP +Most fields in the date and time can be defaulted. +The default time zone is normally \*u, but this can be overridden by the +.B \-z +option. +The other defaults are determined in the order year, month, day, +hour, minute, and second (most to least significant). At least one of these +fields must be provided. For omitted fields that are of higher significance +than the highest provided field, the time zone's current values are assumed. +For all other omitted fields, +the lowest possible values are assumed. +For example, without +.BR \-z , +the date +.B "20, 10:30" +defaults to +10:30:00 \*u of the 20th of the \*u time zone's current month and year. +The date/time must be quoted if it contains spaces. +.RE +.TP +.BR \-M [\f2rev\fP] +Set the modification time on the new working file +to be the date of the retrieved revision. +Use this option with care; it can confuse +.BR make (1). +.TP +.BI \-s state +retrieves the latest revision on the selected branch whose state is set to +.IR state . +.TP +.B \-T +Preserve the modification time on the \*r file +even if the \*r file changes because a lock is added or removed. +This option can suppress extensive recompilation caused by a +.BR make (1) +dependency of some other copy of the working file on the \*r file. +Use this option with care; it can suppress recompilation even when it is needed, +i.e. when the change of lock +would mean a change to keyword strings in the other working file. +.TP +.BR \-w [\f2login\fP] +retrieves the latest revision on the selected branch which was checked in +by the user with login name +.IR login . +If the argument +.I login +is +omitted, the caller's login is assumed. +.TP +.BI \-j joinlist +generates a new revision which is the join of the revisions on +.IR joinlist . +This option is largely obsoleted by +.BR rcsmerge (1) +but is retained for backwards compatibility. +.RS +.PP +The +.I joinlist +is a comma-separated list of pairs of the form +.IB rev2 : rev3, +where +.I rev2 +and +.I rev3 +are (symbolic or numeric) +revision numbers. +For the initial such pair, +.I rev1 +denotes the revision selected +by the above options +.BR \-f , +\&.\|.\|., +.BR \-w . +For all other pairs, +.I rev1 +denotes the revision generated by the previous pair. +(Thus, the output +of one join becomes the input to the next.) +.PP +For each pair, +.B co +joins revisions +.I rev1 +and +.I rev3 +with respect to +.IR rev2 . +This means that all changes that transform +.I rev2 +into +.I rev1 +are applied to a copy of +.IR rev3 . +This is particularly useful if +.I rev1 +and +.I rev3 +are the ends of two branches that have +.I rev2 +as a common ancestor. If +.IR rev1 < rev2 < rev3 +on the same branch, +joining generates a new revision which is like +.I rev3, +but with all changes that lead from +.I rev1 +to +.I rev2 +undone. +If changes from +.I rev2 +to +.I rev1 +overlap with changes from +.I rev2 +to +.I rev3, +.B co +reports overlaps as described in +.BR merge (1). +.PP +For the initial pair, +.I rev2 +can be omitted. The default is the common +ancestor. +If any of the arguments indicate branches, the latest revisions +on those branches are assumed. +The options +.B \-l +and +.B \-u +lock or unlock +.IR rev1 . +.RE +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.I n, +where +.I n +can be +.BR 3 , +.BR 4 , +or +.BR 5 . +This can be useful when interchanging \*r files with others who are +running older versions of \*r. +To see which version of \*r your correspondents are running, have them invoke +.BR "rcs \-V" ; +this works with newer versions of \*r. +If it doesn't work, have them invoke +.B rlog +on an \*r file; +if none of the first few lines of output contain the string +.B branch: +it is version 3; +if the dates' years have just two digits, it is version 4; +otherwise, it is version 5. +An \*r file generated while emulating version 3 loses its default branch. +An \*r revision generated while emulating version 4 or earlier has +a time stamp that is off by up to 13 hours. +A revision extracted while emulating version 4 or earlier contains +abbreviated dates of the form +.IB yy / mm / dd +and can also contain different white space and line prefixes +in the substitution for +.BR $\&Log$ . +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.TP +.BI \-z zone +specifies the date output format in keyword substitution, +and specifies the default time zone for +.I date +in the +.BI \-d date +option. +The +.I zone +should be empty, a numeric \*u offset, or the special string +.B LT +for local time. +The default is an empty +.IR zone , +which uses the traditional \*r format of \*u without any time zone indication +and with slashes separating the parts of the date; +otherwise, times are output in \*i 8601 format with time zone indication. +For example, if local time is January 11, 1990, 8pm Pacific Standard Time, +eight hours west of \*u, +then the time is output as follows: +.RS +.LP +.RS +.nf +.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u +.ne 4 +\f2option\fP \f2time output\fP +\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP +\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP +\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP +.ta 4n +4n +4n +4n +.fi +.RE +.LP +The +.B \-z +option does not affect dates stored in \*r files, +which are always \*u. +.RE +.SH "KEYWORD SUBSTITUTION" +Strings of the form +.BI $ keyword $ +and +.BI $ keyword : .\|.\|. $ +embedded in +the text are replaced +with strings of the form +.BI $ keyword : value $ +where +.I keyword +and +.I value +are pairs listed below. +Keywords can be embedded in literal strings +or comments to identify a revision. +.PP +Initially, the user enters strings of the form +.BI $ keyword $ . +On checkout, +.B co +replaces these strings with strings of the form +.BI $ keyword : value $ . +If a revision containing strings of the latter form +is checked back in, the value fields will be replaced during the next +checkout. +Thus, the keyword values are automatically updated on checkout. +This automatic substitution can be modified by the +.B \-k +options. +.PP +Keywords and their corresponding values: +.TP +.B $\&Author$ +The login name of the user who checked in the revision. +.TP +.B $\&Date$ +The date and time the revision was checked in. +With +.BI \-z zone +a numeric time zone offset is appended; otherwise, the date is \*u. +.TP +.B $\&Header$ +A standard header containing the full pathname of the \*r file, the +revision number, the date and time, the author, the state, +and the locker (if locked). +With +.BI \-z zone +a numeric time zone offset is appended to the date; otherwise, the date is \*u. +.TP +.B $\&Id$ +Same as +.BR $\&Header$ , +except that the \*r filename is without a path. +.TP +.B $\&Locker$ +The login name of the user who locked the revision (empty if not locked). +.TP +.B $\&Log$ +The log message supplied during checkin, preceded by a header +containing the \*r filename, the revision number, the author, and the date +and time. +With +.BI \-z zone +a numeric time zone offset is appended; otherwise, the date is \*u. +Existing log messages are +.I not +replaced. +Instead, the new log message is inserted after +.BR $\&Log: .\|.\|. $ . +This is useful for +accumulating a complete change log in a source file. +.RS +.LP +Each inserted line is prefixed by the string that prefixes the +.B $\&Log$ +line. For example, if the +.B $\&Log$ +line is +.RB \*(lq "//\ $\&Log: tan.cc\ $" \*(rq, +\*r prefixes each line of the log with +.RB \*(lq "//\ " \*(rq. +This is useful for languages with comments that go to the end of the line. +The convention for other languages is to use a +.RB \*(lq " \(** " \(rq +prefix inside a multiline comment. +For example, the initial log comment of a C program +conventionally is of the following form: +.RS +.LP +.nf +.ft 3 +.ne 3 +/\(** +.in +\w'/'u +\(** $\&Log$ +\(**/ +.in +.ft +.fi +.RE +.LP +For backwards compatibility with older versions of \*r, if the log prefix is +.B /\(** +or +.B (\(** +surrounded by optional white space, inserted log lines contain a space +instead of +.B / +or +.BR ( ; +however, this usage is obsolescent and should not be relied on. +.RE +.TP +.B $\&Name$ +The symbolic name used to check out the revision, if any. +For example, +.B "co\ \-rJoe" +generates +.BR "$\&Name:\ Joe\ $" . +Plain +.B co +generates just +.BR "$\&Name:\ \ $" . +.TP +.B $\&RCSfile$ +The name of the \*r file without a path. +.TP +.B $\&Revision$ +The revision number assigned to the revision. +.TP +.B $\&Source$ +The full pathname of the \*r file. +.TP +.B $\&State$ +The state assigned to the revision with the +.B \-s +option of +.BR rcs (1) +or +.BR ci (1). +.PP +The following characters in keyword values are represented by escape sequences +to keep keyword strings well-formed. +.LP +.RS +.nf +.ne 6 +.ta \w'newline 'u +\f2char escape sequence\fP +tab \f3\et\fP +newline \f3\en\fP +space \f3\e040 +$ \e044 +\e \e\e\fP +.fi +.RE +.SH "FILE MODES" +The working file inherits the read and execute permissions from the \*r +file. In addition, the owner write permission is turned on, unless +.B \-kv +is set or the file +is checked out unlocked and locking is set to strict (see +.BR rcs (1)). +.PP +If a file with the name of the working file exists already and has write +permission, +.B co +aborts the checkout, +asking beforehand if possible. +If the existing working file is +not writable or +.B \-f +is given, the working file is deleted without asking. +.SH FILES +.B co +accesses files much as +.BR ci (1) +does, except that it does not need to read the working file +unless a revision number of +.B $ +is specified. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +The \*r pathname, the working pathname, +and the revision number retrieved are +written to the diagnostic output. +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +rcsintro(1), ci(1), ctime(3), date(1), ident(1), make(1), +rcs(1), rcsclean(1), rcsdiff(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH LIMITS +Links to the \*r and working files are not preserved. +.PP +There is no way to selectively suppress the expansion of keywords, except +by writing them differently. In nroff and troff, this is done by embedding the +null-character +.B \e& +into the keyword. +.br diff --git a/gnu/usr.bin/rcs/co/co.c b/gnu/usr.bin/rcs/co/co.c new file mode 100644 index 00000000000..51cb2b27792 --- /dev/null +++ b/gnu/usr.bin/rcs/co/co.c @@ -0,0 +1,826 @@ +/* Check out working files from revisions of RCS files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.18 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.17 1995/06/01 16:23:43 eggert + * (main, preparejoin): Pass argument instead of using `join' static variable. + * (main): Add -kb. + * + * Revision 5.16 1994/03/17 14:05:48 eggert + * Move buffer-flushes out of critical sections, since they aren't critical. + * Use ORCSerror to clean up after a fatal error. Remove lint. + * Specify subprocess input via file descriptor, not file name. + * + * Revision 5.15 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.14 1993/11/03 17:42:27 eggert + * Add -z. Generate a value for the Name keyword. + * Don't arbitrarily limit the number of joins. + * Improve quality of diagnostics. + * + * Revision 5.13 1992/07/28 16:12:44 eggert + * Add -V. Check that working and RCS files are distinct. + * + * Revision 5.12 1992/02/17 23:02:08 eggert + * Add -T. + * + * Revision 5.11 1992/01/24 18:44:19 eggert + * Add support for bad_creat0. lint -> RCS_lint + * + * Revision 5.10 1992/01/06 02:42:34 eggert + * Update usage string. + * + * Revision 5.9 1991/10/07 17:32:46 eggert + * -k affects just working file, not RCS file. + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Warn before removing somebody else's file. + * Add -M. Fix co -j bugs. Tune. + * + * Revision 5.7 1991/04/21 11:58:15 eggert + * Ensure that working file is newer than RCS file after co -[lu]. + * Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.6 1990/12/04 05:18:38 eggert + * Don't checkaccesslist() unless necessary. + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.5 1990/11/01 05:03:26 eggert + * Fix -j. Add -I. + * + * Revision 5.4 1990/10/04 06:30:11 eggert + * Accumulate exit status across files. + * + * Revision 5.3 1990/09/11 02:41:09 eggert + * co -kv yields a readonly working file. + * + * Revision 5.2 1990/09/04 08:02:13 eggert + * Standardize yes-or-no procedure. + * + * Revision 5.0 1990/08/22 08:10:02 eggert + * Permit multiple locks by same user. Add setuid support. + * Remove compile-time limits; use malloc instead. + * Permit dates past 1999/12/31. Switch to GMT. + * Make lock and temp files faster and safer. + * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. + * + * Revision 4.7 89/05/01 15:11:41 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.6 88/08/09 19:12:15 eggert + * Fix "co -d" core dump; rawdate wasn't always initialized. + * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint + * + * Revision 4.5 87/12/18 11:35:40 narten + * lint cleanups (from Guy Harris) + * + * Revision 4.4 87/10/18 10:20:53 narten + * Updating version numbers changes relative to 1.1, are actually + * relative to 4.2 + * + * Revision 1.3 87/09/24 13:58:30 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:21:38 jenkins + * Port to suns + * + * Revision 4.2 83/12/05 13:39:48 wft + * made rewriteflag external. + * + * Revision 4.1 83/05/10 16:52:55 wft + * Added option -u and -f. + * Added handling of default branch. + * Replaced getpwuid() with getcaller(). + * Removed calls to stat(); now done by pairfilenames(). + * Changed and renamed rmoldfile() to rmworkfile(). + * Replaced catchints() calls with restoreints(), unlink()--link() with rename(); + * + * Revision 3.7 83/02/15 15:27:07 wft + * Added call to fastcopy() to copy remainder of RCS file. + * + * Revision 3.6 83/01/15 14:37:50 wft + * Added ignoring of interrupts while RCS file is renamed; this avoids + * deletion of RCS files during the unlink/link window. + * + * Revision 3.5 82/12/08 21:40:11 wft + * changed processing of -d to use DATEFORM; removed actual from + * call to preparejoin; re-fixed printing of done at the end. + * + * Revision 3.4 82/12/04 18:40:00 wft + * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. + * Fixed printing of "done". + * + * Revision 3.3 82/11/28 22:23:11 wft + * Replaced getlogin() with getpwuid(), flcose() with ffclose(), + * %02d with %.2d, mode generation for working file with WORKMODE. + * Fixed nil printing. Fixed -j combined with -l and -p, and exit + * for non-existing revisions in preparejoin(). + * + * Revision 3.2 82/10/18 20:47:21 wft + * Mode of working file is now maintained even for co -l, but write permission + * is removed. + * The working file inherits its mode from the RCS file, plus write permission + * for the owner. The write permission is not given if locking is strict and + * co does not lock. + * An existing working file without write permission is deleted automatically. + * Otherwise, co asks (empty answer: abort co). + * Call to getfullRCSname() added, check for write error added, call + * for getlogin() fixed. + * + * Revision 3.1 82/10/13 16:01:30 wft + * fixed type of variables receiving from getc() (char -> int). + * removed unused variables. + */ + + + + +#include "rcsbase.h" + +static char *addjoin P((char*)); +static char const *getancestor P((char const*,char const*)); +static int buildjoin P((char const*)); +static int preparejoin P((char*)); +static int rmlock P((struct hshentry const*)); +static int rmworkfile P((void)); +static void cleanup P((void)); + +static char const quietarg[] = "-q"; + +static char const *expandarg, *suffixarg, *versionarg, *zonearg; +static char const **joinlist; /* revisions to be joined */ +static int joinlength; +static FILE *neworkptr; +static int exitstatus; +static int forceflag; +static int lastjoin; /* index of last element in joinlist */ +static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */ +static int mtimeflag; +static struct hshentries *gendeltas; /* deltas to be generated */ +static struct hshentry *targetdelta; /* final delta to be generated */ +static struct stat workstat; + +mainProg(coId, "co", "$FreeBSD$") +{ + static char const cmdusage[] = + "\nco usage: co -{fIlMpqru}[rev] -ddate -jjoins -ksubst -sstate -T -w[who] -Vn -xsuff -zzone file ..."; + + char *a, *joinflag, **newargv; + char const *author, *date, *rev, *state; + char const *joinname, *newdate, *neworkname; + int changelock; /* 1 if a lock has been changed, -1 if error */ + int expmode, r, tostdout, workstatstat; + int Ttimeflag; + struct buf numericrev; /* expanded revision number */ + char finaldate[datesize]; +# if OPEN_O_BINARY + int stdout_mode = 0; +# endif + + setrid(); + author = date = rev = state = 0; + joinflag = 0; + bufautobegin(&numericrev); + expmode = -1; + suffixes = X_DEFAULT; + tostdout = false; + Ttimeflag = false; + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + while (a = *++argv, 0<--argc && *a++=='-') { + switch (*a++) { + + case 'r': + revno: + if (*a) { + if (rev) warn("redefinition of revision number"); + rev = a; + } + break; + + case 'f': + forceflag=true; + goto revno; + + case 'l': + if (lockflag < 0) { + warn("-u overridden by -l."); + } + lockflag = 1; + goto revno; + + case 'u': + if (0 < lockflag) { + warn("-l overridden by -u."); + } + lockflag = -1; + goto revno; + + case 'p': + tostdout = true; + goto revno; + + case 'I': + interactiveflag = true; + goto revno; + + case 'q': + quietflag=true; + goto revno; + + case 'd': + if (date) + redefined('d'); + str2date(a, finaldate); + date=finaldate; + break; + + case 'j': + if (*a) { + if (joinflag) redefined('j'); + joinflag = a; + } + break; + + case 'M': + mtimeflag = true; + goto revno; + + case 's': + if (*a) { + if (state) redefined('s'); + state = a; + } + break; + + case 'T': + if (*a) + goto unknown; + Ttimeflag = true; + break; + + case 'w': + if (author) redefined('w'); + if (*a) + author = a; + else + author = getcaller(); + break; + + case 'x': + suffixarg = *argv; + suffixes = a; + break; + + case 'V': + versionarg = *argv; + setRCSversion(versionarg); + break; + + case 'z': + zonearg = *argv; + zone_set(a); + break; + + case 'k': /* set keyword expand mode */ + expandarg = *argv; + if (0 <= expmode) redefined('k'); + if (0 <= (expmode = str2expmode(a))) + break; + /* fall into */ + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + + }; + } /* end of option processing */ + + /* Now handle all pathnames. */ + if (nerror) cleanup(); + else if (argc < 1) faterror("no input file%s", cmdusage); + else for (; 0 < argc; cleanup(), ++argv, --argc) { + ffree(); + + if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0) + continue; + + /* + * RCSname contains the name of the RCS file, and finptr + * points at it. workname contains the name of the working file. + * Also, RCSstat has been set. + */ + diagnose("%s --> %s\n", RCSname, tostdout?"standard output":workname); + + workstatstat = -1; + if (tostdout) { +# if OPEN_O_BINARY + int newmode = Expand==BINARY_EXPAND ? OPEN_O_BINARY : 0; + if (stdout_mode != newmode) { + stdout_mode = newmode; + oflush(); + VOID setmode(STDOUT_FILENO, newmode); + } +# endif + neworkname = 0; + neworkptr = workstdout = stdout; + } else { + workstatstat = stat(workname, &workstat); + if (workstatstat == 0 && same_file(RCSstat, workstat, 0)) { + rcserror("RCS file is the same as working file %s.", + workname + ); + continue; + } + neworkname = makedirtemp(1); + if (!(neworkptr = fopenSafer(neworkname, FOPEN_W_WORK))) { + if (errno == EACCES) + workerror("permission denied on parent directory"); + else + eerror(neworkname); + continue; + } + } + + gettree(); /* reads in the delta tree */ + + if (!Head) { + /* no revisions; create empty file */ + diagnose("no revisions present; generating empty revision 0.0\n"); + if (lockflag) + warn( + "no revisions, so nothing can be %slocked", + lockflag < 0 ? "un" : "" + ); + Ozclose(&fcopy); + if (workstatstat == 0) + if (!rmworkfile()) continue; + changelock = 0; + newdate = 0; + } else { + int locks = lockflag ? findlock(false, &targetdelta) : 0; + if (rev) { + /* expand symbolic revision number */ + if (!expandsym(rev, &numericrev)) + continue; + } else { + switch (locks) { + default: + continue; + case 0: + bufscpy(&numericrev, Dbranch?Dbranch:""); + break; + case 1: + bufscpy(&numericrev, targetdelta->num); + break; + } + } + /* get numbers of deltas to be generated */ + if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas))) + continue; + /* check reservations */ + changelock = + lockflag < 0 ? + rmlock(targetdelta) + : lockflag == 0 ? + 0 + : + addlock(targetdelta, true); + + if ( + changelock < 0 + || (changelock && !checkaccesslist()) + || dorewrite(lockflag, changelock) != 0 + ) + continue; + + if (0 <= expmode) + Expand = expmode; + if (0 < lockflag && Expand == VAL_EXPAND) { + rcserror("cannot combine -kv and -l"); + continue; + } + + if (joinflag && !preparejoin(joinflag)) + continue; + + diagnose("revision %s%s\n",targetdelta->num, + 0name = namedrev(rev, targetdelta); + joinname = buildrevision( + gendeltas, targetdelta, + joinflag&&tostdout ? (FILE*)0 : neworkptr, + Expand < MIN_UNEXPAND + ); +# if !large_memory + if (fcopy == neworkptr) + fcopy = 0; /* Don't close it twice. */ +# endif + if_advise_access(changelock && gendeltas->first!=targetdelta, + finptr, MADV_SEQUENTIAL + ); + + if (donerewrite(changelock, + Ttimeflag ? RCSstat.st_mtime : (time_t)-1 + ) != 0) + continue; + + if (changelock) { + locks += lockflag; + if (1 < locks) + rcswarn("You now have %d locks.", locks); + } + + newdate = targetdelta->date; + if (joinflag) { + newdate = 0; + if (!joinname) { + aflush(neworkptr); + joinname = neworkname; + } + if (Expand == BINARY_EXPAND) + workerror("merging binary files"); + if (!buildjoin(joinname)) + continue; + } + } + if (!tostdout) { + mode_t m = WORKMODE(RCSstat.st_mode, + ! (Expand==VAL_EXPAND || (lockflag<=0 && StrictLocks)) + ); + time_t t = mtimeflag&&newdate ? date2time(newdate) : (time_t)-1; + aflush(neworkptr); + ignoreints(); + r = chnamemod(&neworkptr, neworkname, workname, 1, m, t); + keepdirtemp(neworkname); + restoreints(); + if (r != 0) { + eerror(workname); + error("see %s", neworkname); + continue; + } + diagnose("done\n"); + } + } + + tempunlink(); + Ofclose(workstdout); + exitmain(exitstatus); + +} /* end of main (co) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); + ORCSclose(); +# if !large_memory + if (fcopy!=workstdout) Ozclose(&fcopy); +# endif + if (neworkptr!=workstdout) Ozclose(&neworkptr); + dirtempunlink(); +} + +#if RCS_lint +# define exiterr coExit +#endif + void +exiterr() +{ + ORCSerror(); + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + + +/***************************************************************** + * The following routines are auxiliary routines + *****************************************************************/ + + static int +rmworkfile() +/* + * Prepare to remove workname, if it exists, and if + * it is read-only. + * Otherwise (file writable): + * if !quietmode asks the user whether to really delete it (default: fail); + * otherwise failure. + * Returns true if permission is gotten. + */ +{ + if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) { + /* File is writable */ + if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ", + workname, + myself(workstat.st_uid) ? "" : ", and you do not own it" + )) { + error(!quietflag && ttystdin() + ? "checkout aborted" + : "writable %s exists; checkout aborted", workname); + return false; + } + } + /* Actual unlink is done later by caller. */ + return true; +} + + + static int +rmlock(delta) + struct hshentry const *delta; +/* Function: removes the lock held by caller on delta. + * Returns -1 if someone else holds the lock, + * 0 if there is no lock on delta, + * and 1 if a lock was found and removed. + */ +{ register struct rcslock * next, * trail; + char const *num; + struct rcslock dummy; + int whomatch, nummatch; + + num=delta->num; + dummy.nextlock=next=Locks; + trail = &dummy; + while (next) { + whomatch = strcmp(getcaller(), next->login); + nummatch=strcmp(num,next->delta->num); + if ((whomatch==0) && (nummatch==0)) break; + /*found a lock on delta by caller*/ + if ((whomatch!=0)&&(nummatch==0)) { + rcserror("revision %s locked by %s; use co -r or rcs -u", + num, next->login + ); + return -1; + } + trail=next; + next=next->nextlock; + } + if (next) { + /*found one; delete it */ + trail->nextlock=next->nextlock; + Locks=dummy.nextlock; + next->delta->lockedby = 0; + return 1; /*success*/ + } else return 0; /*no lock on delta*/ +} + + + + +/***************************************************************** + * The rest of the routines are for handling joins + *****************************************************************/ + + + static char * +addjoin(joinrev) + char *joinrev; +/* Add joinrev's number to joinlist, yielding address of char past joinrev, + * or 0 if no such revision exists. + */ +{ + register char *j; + register struct hshentry *d; + char terminator; + struct buf numrev; + struct hshentries *joindeltas; + + j = joinrev; + for (;;) { + switch (*j++) { + default: + continue; + case 0: + case ' ': case '\t': case '\n': + case ':': case ',': case ';': + break; + } + break; + } + terminator = *--j; + *j = 0; + bufautobegin(&numrev); + d = 0; + if (expandsym(joinrev, &numrev)) + d = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&joindeltas); + bufautoend(&numrev); + *j = terminator; + if (d) { + joinlist[++lastjoin] = d->num; + return j; + } + return 0; +} + + static int +preparejoin(j) + register char *j; +/* Parse join list J and place pointers to the + * revision numbers into joinlist. + */ +{ + lastjoin= -1; + for (;;) { + while ((*j==' ')||(*j=='\t')||(*j==',')) j++; + if (*j=='\0') break; + if (lastjoin>=joinlength-2) { + joinlist = + (joinlength *= 2) == 0 + ? tnalloc(char const *, joinlength = 16) + : trealloc(char const *, joinlist, joinlength); + } + if (!(j = addjoin(j))) return false; + while ((*j==' ') || (*j=='\t')) j++; + if (*j == ':') { + j++; + while((*j==' ') || (*j=='\t')) j++; + if (*j!='\0') { + if (!(j = addjoin(j))) return false; + } else { + rcsfaterror("join pair incomplete"); + } + } else { + if (lastjoin==0) { /* first pair */ + /* common ancestor missing */ + joinlist[1]=joinlist[0]; + lastjoin=1; + /*derive common ancestor*/ + if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1]))) + return false; + } else { + rcsfaterror("join pair incomplete"); + } + } + } + if (lastjoin < 1) + rcsfaterror("empty join"); + return true; +} + + + + static char const * +getancestor(r1, r2) + char const *r1, *r2; +/* Yield the common ancestor of r1 and r2 if successful, 0 otherwise. + * Work reliably only if r1 and r2 are not branch numbers. + */ +{ + static struct buf t1, t2; + + int l1, l2, l3; + char const *r; + + l1 = countnumflds(r1); + l2 = countnumflds(r2); + if ((22 ? 2 : l1); + VOID partialno(&t2, r2, l2>2 ? 2 : l2); + r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string; + if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0) + return r; + } else if (cmpnumfld(r1, r2, l3+1)!=0) + return partialno(&t1,r1,l3); + } + rcserror("common ancestor of %s and %s undefined", r1, r2); + return 0; +} + + + + static int +buildjoin(initialfile) + char const *initialfile; +/* Function: merge pairs of elements in joinlist into initialfile + * If workstdout is set, copy result to stdout. + * All unlinking of initialfile, rev2, and rev3 should be done by tempunlink(). + */ +{ + struct buf commarg; + struct buf subs; + char const *rev2, *rev3; + int i; + char const *cov[10], *mergev[11]; + char const **p; + + bufautobegin(&commarg); + bufautobegin(&subs); + rev2 = maketemp(0); + rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ + + cov[1] = CO; + /* cov[2] setup below */ + p = &cov[3]; + if (expandarg) *p++ = expandarg; + if (suffixarg) *p++ = suffixarg; + if (versionarg) *p++ = versionarg; + if (zonearg) *p++ = zonearg; + *p++ = quietarg; + *p++ = RCSname; + *p = 0; + + mergev[1] = MERGE; + mergev[2] = mergev[4] = "-L"; + /* rest of mergev setup below */ + + i=0; + while (inum); + else { + bufscat(&subs, ","); + bufscat(&subs, joinlist[i-2]); + bufscat(&subs, ":"); + bufscat(&subs, joinlist[i-1]); + } + diagnose("revision %s\n",joinlist[i]); + bufscpy(&commarg, "-p"); + bufscat(&commarg, joinlist[i]); + cov[2] = commarg.string; + if (runv(-1, rev2, cov)) + goto badmerge; + diagnose("revision %s\n",joinlist[i+1]); + bufscpy(&commarg, "-p"); + bufscat(&commarg, joinlist[i+1]); + cov[2] = commarg.string; + if (runv(-1, rev3, cov)) + goto badmerge; + diagnose("merging...\n"); + mergev[3] = subs.string; + mergev[5] = joinlist[i+1]; + p = &mergev[6]; + if (quietflag) *p++ = quietarg; + if (lastjoin<=i+2 && workstdout) *p++ = "-p"; + *p++ = initialfile; + *p++ = rev2; + *p++ = rev3; + *p = 0; + switch (runv(-1, (char*)0, mergev)) { + case DIFF_FAILURE: case DIFF_SUCCESS: + break; + default: + goto badmerge; + } + i=i+2; + } + bufautoend(&commarg); + bufautoend(&subs); + return true; + + badmerge: + nerror++; + bufautoend(&commarg); + bufautoend(&subs); + return false; +} diff --git a/gnu/usr.bin/rcs/doc/rcs.ms b/gnu/usr.bin/rcs/doc/rcs.ms new file mode 100644 index 00000000000..f6b0db30033 --- /dev/null +++ b/gnu/usr.bin/rcs/doc/rcs.ms @@ -0,0 +1,1518 @@ +.\" Format this file with: +.\" pic file | tbl | troff -ms +.\" +.\" \*s stands for $, and avoids problems when this file is checked in. +.ds s $ +.de D( +.DS +.nr VS 12p +.vs 12p +.I +.. +.de D) +.DE +.nr VS 18p +.vs 18p +.R +.. +.de Id +.ND \\$4 +.. +.Id $FreeBSD$ +.RP +.TL +RCS\*-A System for Version Control +.sp +.AU +Walter F. Tichy +.AI +Department of Computer Sciences +Purdue University +West Lafayette, Indiana 47907 +.sp +.AB +An important problem in program development and maintenance is version control, +i.e., the task of keeping a software system consisting of many versions and +configurations well organized. +The Revision Control System (RCS) +is a software tool that assists with that task. +RCS manages revisions of text documents, in particular source programs, +documentation, and test data. +It automates the storing, retrieval, logging and identification of revisions, +and it provides selection mechanisms for composing configurations. +This paper introduces basic version control concepts and +discusses the practice of version control +using RCS. +For conserving space, RCS stores deltas, i.e., differences between +successive revisions. Several delta storage methods are discussed. +Usage statistics show that RCS's delta storage method is +space and time efficient. +The paper concludes with a detailed survey of version control tools. +.sp +\fBKeywords\fR: configuration management, history management, +version control, revisions, deltas. +.AE +.FS +An earlier version of this paper was published in +.I "Software\*-Practice & Experience" +.B 15 , +7 (July 1985), 637-654. +.FE +.nr VS 18p +.LP +.NH +Introduction +.PP +Version control is the task of keeping software +systems consisting of many versions and configurations well organized. +The Revision Control System (RCS) is a set of UNIX +commands that assist with that task. +.PP +RCS' primary function is to manage \fIrevision groups\fR. +A revision group is a set of text documents, called \fIrevisions\fR, +that evolved from each other. A new revision is +created by manually editing an existing one. +RCS organizes the revisions into an ancestral tree. The initial revision +is the root of the tree, and the tree edges indicate +from which revision a given one evolved. +Besides managing individual revision groups, RCS provides +flexible selection functions for composing configurations. +RCS may be combined with MAKE\u1\d, +resulting in a powerful package for version control. +.PP +RCS also offers facilities for +merging updates with customer modifications, +for distributed software development, and +for automatic identification. +Identification is the `stamping' +of revisions and configurations with unique markers. +These markers are akin to serial numbers, +telling software maintainers unambiguously which configuration +is before them. +.PP +RCS is designed for both production and experimental +environments. +In production environments, +access controls detect update conflicts and prevent overlapping changes. +In experimental environments, where strong controls are +counterproductive, it is possible to loosen the controls. +.PP +Although RCS was originally intended for programs, it is useful for any +text that is revised frequently and whose previous revisions must be +preserved. RCS has been applied successfully to store the source +text for drawings, VLSI layouts, documentation, specifications, +test data, form letters and articles. +.PP +This paper discusses the practice of +version control using RCS. +It also introduces basic version control concepts, +useful for clarifying current practice and designing similar systems. +Revision groups of individual components are treated in the next three sections, +and the extensions to configurations follow. +Because of its size, a survey of version control tools +appears at the end of the paper. +.NH +Getting started with RCS +.PP +Suppose a text file \fIf.c\fR is to be placed under control of RCS. +Invoking the check-in command +.D( +ci f.c +.D) +creates a new revision group with the contents of +\fIf.c\fR as the initial +revision (numbered 1.1) +and stores the group into the file \fIf.c,v\fR. +Unless told otherwise, the command deletes \fIf.c\fR. +It also asks for a description of the group. +The description should state the common purpose of all revisions in the group, +and becomes part of the group's documentation. +All later check-in commands will ask for a log entry, +which should summarize the changes made. +(The first revision is assigned a default log message, +which just records the fact that it is the initial revision.) +.PP +Files ending in \fI,v\fR +are called \fIRCS files\fR (\fIv\fR stands for \fIv\fRersions); +the others are called working files. +To get back the working file \fIf.c\fR in the previous example, +execute the check-out command: +.D( +co f.c +.D) +.R +This command extracts the latest revision from +the revision group \fIf.c,v\fR and writes +it into \fIf.c\fR. +The file \fIf.c\fR can now be edited and, when finished, +checked back in with \fIci\fR: +.D( +ci f.c +.D) +\fICi\fR assigns number 1.2 to +the new revision. +If \fIci\fR complains with the message +.D( +ci error: no lock set by +.D) +then the system administrator has decided to configure RCS for a +production environment by enabling the `strict locking feature'. +If this feature is enabled, all RCS files are initialized +such that check-in operations require a lock on the previous revision +(the one from which the current one evolved). +Locking prevents overlapping modifications if several people work on the same file. +If locking is required, the revision should +have been locked during the check-out by using +the option \fI\-l\fR: +.D( +co \-l f.c +.D) +Of course it is too late now for the check-out with locking, because +\fIf.c\fR has already been changed; checking out the file again +would overwrite the modifications. +(To prevent accidental overwrites, \fIco\fR senses the presence +of a working file and asks whether the user really intended to overwrite it. +The overwriting check-out is sometimes useful for +backing up to the previous revision.) +To be able to proceed with the check-in in the present case, first execute +.D( +rcs \-l f.c +.D) +This command retroactively locks the latest revision, unless someone +else locked it in the meantime. In this case, the two programmers +involved have to negotiate whose +modifications should take precedence. +.PP +If an RCS file is private, i.e., if only the owner of the file is expected +to deposit revisions into it, the strict locking feature is unnecessary and +may be disabled. +If strict locking is disabled, +the owner of the RCS file need not have a lock for check-in. +For safety reasons, all others +still do. Turning strict locking off and on is done with the commands: +.D( +rcs \-U f.c \fRand\fP rcs \-L f.c +.D) +These commands enable or disable the strict locking feature for each RCS file +individually. +The system administrator only decides whether strict locking is +enabled initially. +.PP +To reduce the clutter in a working directory, all RCS files can be moved +to a subdirectory with the name \fIRCS\fR. +RCS commands look first into that directory for RCS files. +All the commands presented above work +with the \fIRCS\fR subdirectory without change.\(dg +.FS \(dg +Pairs of RCS and working files can actually be specified in 3 ways: +a) both are given, b) only the working file is given, c) only the +RCS file is given. +If a pair is given, both files may have arbitrary path prefixes; +RCS commands pair them up intelligently. +.FE +.PP +It may be undesirable that \fIci\fR deletes the working file. +For instance, sometimes one would like to save the current revision, +but continue editing. +Invoking +.D( +ci \-l f.c +.D) +checks in \fIf.c\fR as usual, but performs an additional +check-out with locking afterwards. Thus, the working file does +not disappear after the check-in. +Similarly, the option +\fI\-u\fR does a check-in followed by a check-out without +locking. This option is useful if the file is needed for compilation after the check-in. +Both options update the identification markers in the working file +(see below). +.PP +Besides the operations \fIci\fR and \fIco\fR, RCS provides the following +commands: +.sp 0 +.nr VS 12p +.vs 12p +.TS +tab(%); +li l. +ident%extract identification markers +rcs%change RCS file attributes +rcsclean%remove unchanged working files (optional) +rcsdiff%compare revisions +rcsfreeze%record a configuration (optional) +rcsmerge%merge revisions +rlog%read log messages and other information in RCS files +.TE +A synopsis of these commands appears in the Appendix. +.NH 2 +Automatic Identification +.PP +RCS can stamp source and object code with special identification strings, +similar to product and serial numbers. +To obtain such identification, place the marker +.D( +\*sId\*s +.D) +into the text of a revision, for instance inside a comment. +The check-out operation will replace this marker with a string of the form +.D( +\*sId: filename revisionnumber date time author state locker \*s +.D) +This string need never be touched, because \fIco\fR keeps it +up to date automatically. +To propagate the marker into object code, simply put +it into a literal character string. In C, this is done as follows: +.D( +static char rcsid[] = \&"\*sId\*s\&"; +.D) +The command \fIident\fR extracts such markers from any file, in particular from +object code. +\fIIdent\fR helps to find out +which revisions of which modules were used in a given program. +It returns a complete and unambiguous component list, +from which a copy of the program can be reconstructed. +This facility is invaluable for program maintenance. +.PP +There are several additional identification markers, one for each component +of \*sId\*s. +The marker +.D( +\*sLog\*s +.D) +has a similar function. It accumulates +the log messages that are requested during check-in. +Thus, one can maintain the complete history of a revision directly inside it, +by enclosing it in a comment. +Figure 1 is an edited version of a log contained in revision 4.1 of +the file \fIci.c\fR. The log appears at the beginning of the file, +and makes it easy to determine what the recent modifications were. +.sp +.nr VS 12p +.vs 12p +.ne 18 +.nf +.in +0.5i +/* +.in +\w'/'u +* \*sLog: ci.c,v \*s +* Revision 4.1 1983/05/10 17:03:06 wft +* Added option \-d and \-w, and updated assignment of date, etc. to new delta. +* Added handling of default branches. +* +* Revision 3.9 1983/02/15 15:25:44 wft +* Added call to fastcopy() to copy remainder of RCS file. +* +* Revision 3.8 1983/01/14 15:34:05 wft +* Added ignoring of interrupts while new RCS file is renamed; +* avoids deletion of RCS files by interrupts. +* +* Revision 3.7 1982/12/10 16:09:20 wft +* Corrected checking of return code from diff. +* An RCS file now inherits its mode during the first ci from the working file, +* except that write permission is removed. +*/ +.in 0 +.ce 1 +Figure 1. Log entries produced by the marker \*sLog\*s. +.fi +.nr VS 18p +.vs 18p +.sp 0 +.LP +Since revisions are stored in the form of differences, +each log message is +physically stored once, +independent of the number of revisions present. +Thus, the \*sLog\*s marker incurs negligible space overhead. +.NH +The RCS Revision Tree +.PP +RCS arranges revisions in an ancestral tree. +The \fIci\fR command builds this tree; the auxiliary command \fIrcs\fR +prunes it. +The tree has a root revision, normally numbered 1.1, and successive revisions +are numbered 1.2, 1.3, etc. The first field of a revision number +is called the \fIrelease number\fR and the second one +the \fIlevel number\fR. Unless given explicitly, +the \fIci\fR command assigns a new revision number +by incrementing the level number of the previous revision. +The release number must be incremented explicitly, using the +\fI\-r\fR option of \fIci\fR. +Assuming there are revisions 1.1, 1.2, and 1.3 in the RCS file f.c,v, the command +.D( +ci \-r2.1 f.c \fRor\fP ci \-r2 f.c +.D) +assigns the number 2.1 to the new revision. +Later check-ins without the \fI\-r\fR option will assign the numbers 2.2, 2.3, +and so on. +The release number should be incremented only at major transition points +in the development, for instance when a new release of a software product has +been completed. +.NH 2 +When are branches needed? +.PP +A young revision tree is slender: +It consists of only one branch, called the trunk. +As the tree ages, side branches may form. +Branches are needed in the following 4 situations. +.IP "\fITemporary fixes\fR" +.sp 0 +Suppose a tree has 5 revisions grouped in 2 releases, +as illustrated in Figure 2. +Revision 1.3, the last one of release 1, is in operation at customer sites, +while release 2 is in active development. +.ne 4 +.PS 4i +.ps -2 +box "1.1" +arrow +box "1.2" +arrow +box "1.3" +arrow +box "2.1" +arrow +box "2.2" +arrow dashed +.ps +2 +.PE +.ce 1 +Figure 2. A slender revision tree. +.sp 0 +Now imagine a customer requesting a fix of +a problem in revision 1.3, although actual development has moved on +to release 2. RCS does not permit an extra +revision to be spliced in between 1.3 and 2.1, since that would not reflect +the actual development history. Instead, create a branch +at revision 1.3, and check in the fix on that branch. +The first branch starting at 1.3 has number 1.3.1, and +the revisions on that branch are numbered 1.3.1.1, 1.3.1.2, etc. +The double numbering is needed to allow for another +branch at 1.3, say 1.3.2. +Revisions on the second branch would be numbered +1.3.2.1, 1.3.2.2, and so on. +The following steps create +branch 1.3.1 and add revision 1.3.1.1: +.sp 0 +.I +.nr VS 12p +.vs 12p +.TS +tab(%); +l l l. + %co \-r1.3 f.c% \*- check out revision 1.3 + %edit f.c% \*- change it + %ci \-r1.3.1 f.c% \*- check it in on branch 1.3.1 +.TE +.nr VS 18p +.vs 18p +.R +This sequence of commands transforms the tree of Figure 2 into +the one in Figure 3. +Note that it may be necessary to incorporate the differences +between 1.3 and 1.3.1.1 +into a revision at level 2. The operation \fIrcsmerge\fR automates this +process (see the Appendix). +.ne 7 +.PS 4i +.ps -2 + box "1.1" + arrow + box "1.2" + arrow +R13: box "1.3" + arrow +R21: box "2.1" + arrow +R22: box "2.2" + arrow dashed + line invis down from R21.s +RB1: box "1.3.1.1" + arrow dashed right from RB1.e + arrow from R13.s to RB1.w +.ps +2 +.PE +.ce 1 +Figure 3. A revision tree with one side branch +.sp +.IP "\fIDistributed development and customer modifications\fR" +.sp 0 +Assume a situation as in Figure 2, where revision 1.3 is in operation +at several customer sites, +while release 2 is in development. +Customer sites should use RCS to store the distributed software. +However, customer modifications should not be placed on the same branch +as the distributed source; instead, they should be placed on a side branch. +When the next software distribution arrives, +it should be appended to the trunk of +the customer's RCS file, and the customer +can then merge the local modifications back into the new release. +In the above example, a +customer's RCS file would contain the following tree, assuming +that the customer has received revision 1.3, added his local modifications +as revision 1.3.1.1, then received revision 2.4, and merged +2.4 and 1.3.1.1, resulting in 2.4.1.1. +.ne 7 +.PS 4i +.ps -2 +R13: box "1.3" + line invis +R21: box invis + line invis +R22: box invis + line invis +R24: box "2.4" + line invis +R25: box invis + line invis + arrow from R13.e to R24.w + line invis down from R21.s +RB1: box "1.3.1.1" + arrow from R13.s to RB1.w + right + line invis down from R25.s +RB2: box "2.4.1.1" + arrow from R24.s to RB2.w +.ps +2 +.PE +.ce 1 +Figure 4. A customer's revision tree with local modifications. +.sp 1 +This approach is actually practiced in the CSNET project, +where several universities and a company cooperate +in developing a national computer network. +.IP "\fIParallel development\fR" +.sp 0 +Sometimes it is desirable to explore an alternate design or +a different implementation technique in parallel with the +main line development. Such development +should be carried out on a side branch. +The experimental changes may later be moved into the main line, or abandoned. +.IP "\fIConflicting updates\fR" +.sp 0 +A common occurrence is that one programmer +has checked out a revision, but cannot complete the assignment +for some reason. In the meantime, another person +must perform another modification +immediately. In that case, the second person should check-out the same revision, +modify it, and check it in on a side branch, for later merging. +.PP +Every node in a revision tree consists of the following attributes: +a revision number, a check-in date and time, the author's identification, +a log entry, a state and the actual text. All these attributes +are determined at the time the revision is checked in. +The state attribute indicates the status of a revision. +It is set automatically to `experimental' during check-in. +A revision can later be promoted to a higher status, for example +`stable' or `released'. The set of states is user-defined. +.NH 2 +Revisions are represented as deltas +.PP +For conserving space, RCS stores revisions in the form +of deltas, i.e., as differences between revisions. +The user interface completely hides this fact. +.PP +A delta is a sequence of edit commands that transforms one string +into another. The deltas employed by RCS are line-based, which means +that the only edit commands allowed are insertion and deletion of lines. +If a single character in a line is changed, the +edit scripts consider the entire line changed. +The program \fIdiff\fR\u2\d +produces a small, line-based delta between pairs of text files. +A character-based edit script would take much longer to compute, +and would not be significantly shorter. +.PP +Using deltas is a classical space-time tradeoff: deltas reduce the +space consumed, but increase access time. +However, a version control tool should impose as little delay +as possible on programmers. +Excessive delays discourage the use of version controls, +or induce programmers to take shortcuts that compromise system integrity. +To gain reasonably fast access time for both editing and compiling, +RCS arranges deltas in the following way. +The most recent revision on the trunk is stored intact. +All other revisions on the trunk are stored as reverse deltas. +A reverse delta describes how to go backward in the development history: +it produces the desired revision if applied to the successor of that revision. +This implementation has the advantage +that extraction of the latest revision is a simple and fast copy +operation. +Adding a new revision to the trunk is also fast: \fIci\fR simply +adds the new revision intact, replaces the previous +revision with a reverse delta, and keeps the rest of the old deltas. +Thus, \fIci\fR requires the computation +of only one new delta. +.PP +Branches need special treatment. The naive solution would be to +store complete copies for the tips of all branches. +Clearly, this approach would cost too much space. Instead, +RCS uses \fIforward\fR deltas for branches. Regenerating a revision +on a side branch proceeds as follows. First, extract the latest revision +on the trunk; secondly, apply reverse deltas until the fork revision for +the branch is obtained; thirdly, apply forward deltas until the desired +branch revision is reached. Figure 5 illustrates a tree with +one side branch. Triangles pointing to the left and right represent +reverse and forward deltas, respectively. +.ne 8 +.PS 4i +.ps -2 +define BD X [line invis $1 right .5; +line up .3 then left .5 down .3 then right .5 down .3 then up .3] X + +define FD X [line invis $1 right .5; +line left .5 down .3 then up .6 then right .5 down .3;] X + +right +D11: BD(" 1.1") + arrow right from D11.e +D12: BD(" 1.2") + arrow right from D12.e +D13: BD(" 1.3") + arrow right from D13.e +D21: BD(" 2.1") + arrow right from D21.e +D22: box "2.2" + line invis down from D21.s +F1: FD("1.3.1.1 ") + arrow from D13.se to F1.w + arrow from F1.e right + right +F2: FD("1.3.1.2 ") +.ps +2 +.PE +.ce 1 +Figure 5. A revision tree with reverse and forward deltas. +.sp 0 +.PP +Although implementing fast check-out for the latest trunk revision, +this arrangement has the disadvantage that generation of other revisions +takes time proportional to the number of deltas applied. For example, +regenerating the branch tip in Figure 5 requires application of five +deltas (including the initial one). Since usage statistics show that +the latest trunk revision is the one that is retrieved in 95 per cent +of all cases (see the section on usage statistics), biasing check-out time +in favor of that revision results in significant savings. +However, careful implementation of the delta application process is +necessary to provide low retrieval overhead for other revisions, in +particular for branch tips. +.PP +There are several techniques for delta application. +The naive one is to pass each delta to a general-purpose text editor. +A prototype of RCS invoked the UNIX editor \fIed\fR both +for applying deltas and for expanding the identification markers. +Although easy to implement, performance was poor, owing to the +high start-up costs and excess generality of \fIed\fR. An intermediate +version of RCS used a special-purpose, stream-oriented editor. +This technique reduced the cost of applying a delta to the cost of +checking out the latest trunk revision. The reason for this behavior +is that each delta application involves a complete pass over +the preceding revision. +.PP +However, there is a much better algorithm. Note that the deltas are +line oriented and that most of the work of a stream editor involves +copying unchanged lines from one revision to the next. A faster +algorithm avoids unnecessary copying of character strings by using +a \fIpiece table\fR. +A piece table is a one-dimensional array, specifying how a given +revision is `pieced together' from lines in the RCS file. +Suppose piece table \fIPT\dr\u\fR represents revision \fIr\fR. +Then \fIPT\dr\u[i]\fR contains the starting position of line \fIi\fR +of revision \fIr\fR. +Application of the next delta transforms piece table \fIPT\dr\u\fR +into \fIPT\dr+1\u\fR. For instance, a delete command removes a +series of entries from the piece table. An insertion command inserts +new entries, moving the entries following the insertion point further down the +array. The inserted entries point to the text lines in the delta. +Thus, no I/O is involved except for reading the delta itself. When all +deltas have been applied to the piece table, a sequential pass +through the table looks up each line in the RCS file and copies it to +the output file, updating identification markers at the same time. +Of course, the RCS file must permit random access, since the copied +lines are scattered throughout that file. Figure 6 illustrates an +RCS file with two revisions and the corresponding piece tables. +.ne 13 +.sp 6 +.ce 1 +\fIFigure 6 is not available.\fP +.sp 5 +.ce 1 +Figure 6. An RCS file and its piece tables +.sp 0 +.PP +The piece table approach has the property that the time for applying a single +delta is roughly determined by the size of the delta, and not by the +size of the revision. For example, if a delta is +10 per cent of the size of a revision, then applying it takes only +10 per cent of the time to generate the latest trunk revision. (The stream +editor would take 100 per cent.) +.PP +There is an important alternative for representing deltas that affects +performance. SCCS\u3\d, +a precursor of RCS, uses \fIinterleaved\fR deltas. +A file containing interleaved deltas is partitioned into blocks of lines. +Each block has a header that specifies to which revision(s) the block +belongs. The blocks are sorted out in such a way that a single +pass over the file can pick up all the lines belonging to a given +revision. Thus, the regeneration time for all revisions is the same: +all headers must be inspected, and the associated blocks either copied +or skipped. As the number of revisions increases, the cost of retrieving +any revision is much higher than the cost of checking out the +latest trunk revision with reverse deltas. A detailed comparison +of SCCS's interleaved deltas and RCS's reverse deltas can be found +in Reference 4. +This reference considers the version of RCS with the +stream editor only. The piece table method improves performance +further, so that RCS is always faster than SCCS, except if 10 +or more deltas are applied. +.PP +Additional speed-up for both delta methods can be obtained by caching +the most recently generated revision, as has been implemented in DSEE.\u5\d +With caching, access time to frequently used revisions can approach normal file +access time, at the cost of some additional space. +.NH +Locking: A Controversial Issue +.PP +The locking mechanism for RCS was difficult to design. +The problem and its solution are first presented in their `pure' form, +followed by a discussion of the complications +caused by `real-world' considerations. +.PP +RCS must prevent two or more persons from depositing competing changes of the +same revision. +Suppose two programmers check out revision 2.4 and +modify it. Programmer A checks in a revision before programmer B\&. +Unfortunately, programmer B has not seen A's +changes, so the effect is that A's changes are covered up by B's deposit. +A's changes are not lost since all revisions +are saved, but they are confined to a single revision.\(dd +.FS \(dd +Note that this problem is entirely different from the atomicity problem. +Atomicity means that +concurrent update operations on the same RCS file cannot be permitted, +because that may result in inconsistent data. +Atomic updates are essential (and implemented in RCS), +but do not solve the conflict discussed here. +.FE +.PP +This conflict is prevented in RCS by locking. +Whenever someone intends to edit a revision (as opposed +to reading or compiling it), the revision should be checked out +and locked, +using the \fI\-l\fR option on \fIco\fR. On subsequent check-in, +\fIci\fR tests the lock and then removes it. +At most one programmer at a time may +lock a particular revision, and only this programmer may check in +the succeeding revision. +Thus, while a revision is locked, it is the exclusive responsibility +of the locker. +.PP +An important maxim for software tools like RCS is that they must +not stand in the way of making progress with a project. +This consideration leads to several weakenings of the locking mechanism. +First of all, even if a revision is locked, it can +still be checked out. This is necessary if other people +wish to compile or inspect the locked revision +while the next one is in preparation. The only operations they +cannot do are to lock the revision or to check in the succeeding one. Secondly, +check-in operations on other branches in the RCS file are still possible; the +locking of one revision does not affect any other revision. +Thirdly, revisions are occasionally locked for a long period of time +because a programmer is absent or otherwise unable to complete +the assignment. If another programmer has to make a pressing change, +there are the following three alternatives for making progress: +a) find out who is holding the lock and ask that person to release it; +b) check out the locked revision, modify it, check it +in on a branch, and merge the changes later; +c) break the lock. Breaking a lock leaves a highly visible +trace, namely an electronic mail message that is sent automatically to the +holder of the lock, recording the breaker and a commentary requested from him. +Thus, breaking locks is tolerated under certain circumstances, +but will not go unnoticed. +Experience has shown that the automatic mail message attaches a high enough +stigma to lock breaking, +such that programmers break locks only in real emergencies, +or when a co-worker resigns and leaves locked revisions behind. +.PP +If an RCS file is private, i.e., when a programmer owns an RCS file +and does not expect anyone else to perform check-in operations, +locking is an unnecessary nuisance. +In this case, +the `strict locking feature' discussed earlier may be disabled, +provided that file protection +is set such that only the owner may write the RCS file. +This has the effect that only the owner can check-in revisions, +and that no lock is needed for doing so. +.PP +As added protection, +each RCS file contains an access list that specifies the users +who may execute update operations. If an access list is empty, +only normal UNIX file protection applies. Thus, the access list is +useful for restricting the set of people who would otherwise have update +permission. Just as with locking, the access list +has no effect on read-only operations such as \fIco\fR. This approach +is consistent with the UNIX philosophy of openness, which contributes +to a productive software development environment. +.NH +Configuration Management +.PP +The preceding sections described how RCS deals with revisions of individual +components; this section discusses how to handle configurations. +A configuration is a set of revisions, where each revision comes +from a different revision group, and the revisions are selected +according to a certain criterion. +For example, +in order to build a functioning compiler, the `right' +revisions from the scanner, the parser, the optimizer +and the code generator must be combined. +RCS, in conjunction with MAKE, +provides a number of facilities to effect a smooth selection. +.NH 2 +RCS Selection Functions +.PP +.IP "\fIDefault selection\fR" +.sp 0 +During development, the usual selection criterion is to choose +the latest revision of all components. The \fIco\fR command +makes this selection by default. For example, the command +.D( +co *,v +.D) +retrieves the latest revision on the default branch of each RCS file +in the current directory. +The default branch is usually the trunk, but may be +set to be a side branch. +Side branches as defaults are needed in distributed software development, +as discussed in the section on the RCS revision tree. +.sp +.IP "\fIRelease based selection\fR" +.sp 0 +Specifying a release or branch number selects the latest revision in +that release or branch. +For instance, +.D( +co \-r2 *,v +.D) +retrieves the latest revision with release number 2 from each RCS file. +This selection is convenient if a release has been completed and +development has moved on to the next release. +.sp +.IP "\fIState and author based selection\fR" +.sp 0 +If the highest level number within a given release number +is not the desired one, +the state attribute can help. For example, +.D( +co \-r2 \-sReleased *,v +.D) +retrieves the latest revision with release number 2 whose state attribute +is `Released'. +Of course, the state attribute has to be set appropriately, using the +\fIci\fR or \fIrcs\fR commands. +Another alternative is to select a revision by its author, +using the \fI\-w\fR option. +.sp +.IP "\fIDate based selection\fR" +.sp 0 +Revisions may also be selected by date. +Suppose a release of an entire system was +completed and current on March 4, at 1:00 p.m. local time. Then the command +.D( +co \-d'March 4, 1:00 pm LT' *,v +.D) +checks out all the components of that release, independent of the numbering. +The \fI\-d\fR option specifies a `cutoff date', i.e., +the revision selected has a check-in date that +is closest to, but not after the date given. +.IP "\fIName based selection\fR" +.sp 0 +The most powerful selection function is based on assigning symbolic +names to revisions and branches. +In large systems, a single release number or date is not sufficient +to collect the appropriate revisions from all groups. +For example, suppose one wishes to combine release 2 +of one subsystem and release 15 of another. +Most likely, the creation dates of those releases differ also. +Thus, a single revision number or date passed to the \fIco\fR command +will not suffice to select the right revisions. +Symbolic revision numbers solve this problem. +Each RCS file may contain a set of symbolic names that are mapped +to numeric revision numbers. For example, assume +the symbol \fIV3\fR is bound to release number 2 in file \fIs,v\fR, and to +revision number 15.9 in \fIt,v\fR. +Then the single command +.D( +co \-rV3 s,v t,v +.D) +retrieves the latest revision of release 2 from \fIs,v\fR, +and revision 15.9 from \fIt,v\fR. +In a large system with many modules, checking out all +revisions with one command greatly simplifies configuration management. +.PP +Judicious use of symbolic revision numbers helps with organizing +large configurations. +A special command, \fIrcsfreeze\fR, +assigns a symbolic revision number to a selected revision +in every RCS file. +\fIRcsfreeze\fR effectively freezes a configuration. +The assigned symbolic revision number selects all components +of the configuration. +If necessary, symbolic numbers +may even be intermixed with numeric ones. Thus, \fIV3.5\fR in the +above example +would select revision 2.5 in \fIs,v\fR and branch 15.9.5 in \fIt,v\fR. +.PP +The options \fI\-r\fR, \fI\-s\fR, \fI\-w\fR and \fI\-d\fR +may be combined. If a branch is given, the latest revision +on that branch satisfying all conditions is retrieved; +otherwise, the default branch is used. +.NH 2 +Combining MAKE and RCS +.PP +MAKE\u1\d +is a program that processes configurations. +It is driven by configuration specifications +recorded in a special file, called a `Makefile'. +MAKE avoids redundant processing steps +by comparing creation dates of source and processed objects. +For example, when instructed to compile all +modules of a given system, it only recompiles +those source modules that were changed +since they were processed last. +.PP +MAKE has been extended with an auto-checkout feature for RCS.* +.FS * +This auto-checkout extension is available only in some versions of MAKE, +e.g. GNU MAKE. +.FE +When a certain file to be processed is not present, +MAKE attempts a check-out operation. +If successful, MAKE performs the required processing, and then deletes +the checked out file to conserve space. +The selection parameters discussed above can be passed to MAKE +either as parameters, or directly embedded in the Makefile. +MAKE has also been extended to search the subdirectory named \fIRCS\fR +for needed files, rather than just the current working directory. +However, if a working file is present, MAKE totally ignores the corresponding +RCS file and uses the working file. +(In newer versions of MAKE distributed by AT&T and others, +auto-checkout can be +achieved with the rule DEFAULT, instead of a special extension of MAKE. +However, a file checked out by the rule DEFAULT +will not be deleted after processing. \fIRcsclean\fR can be +used for that purpose.) +.PP +With auto-checkout, RCS/MAKE can effect a selection rule +especially tuned for multi-person software development and maintenance. +In these situations, +programmers should obtain configurations that consist of +the revisions they have personally checked out plus the latest +checked in revision of all other revision groups. +This schema can be set up as follows. +.PP +Each programmer chooses a working directory +and places into it a symbolic link, named \fIRCS\fR, +to the directory containing the relevant RCS files. +The symbolic link makes sure that \fIco\fR and \fIci\fR +operations need only specify the working files, and that +the Makefile need not be changed. +The programmer then checks out the needed files and modifies them. +If MAKE is invoked, +it composes configurations by selecting those +revisions that are checked out, and the rest from the +subdirectory \fIRCS\fR. +The latter selection may be controlled by a symbolic +revision number or any of the other selection criteria. +If there are several programmers editing in separate working directories, +they are insulated from each other's changes until checking in their +modifications. +.PP +Similarly, a maintainer can recreate an older configuration +by starting to work in an empty working directory. +During the initial MAKE invocation, all revisions are selected from RCS files. +As the maintainer checks out files and modifies them, +a new configuration is gradually built up. +Every time MAKE is invoked, it substitutes the modified revisions +into the configuration being manipulated. +.PP +A final application of RCS is to use it for storing Makefiles. +Revision groups of Makefiles represent +multiple versions of configurations. +Whenever a configuration is baselined or distributed, +the best approach is to unambiguously fix +the configuration with a symbolic revision number by calling +\fIrcsfreeze\fR, +to embed that symbol into the Makefile, and to +check in the Makefile (using the same symbolic revision number). +With this approach, old configurations +can be regenerated easily and reliably. +.NH +Usage Statistics +.PP +The following usage statistics were collected on two DEC VAX-11/780 +computers of the Purdue Computer Science Department. Both machines +are mainly used for research purposes. Thus, the data +reflect an environment in which the majority of projects +involve prototyping and advanced software development, +but relatively little long-term maintenance. +.PP +For the first experiment, +the \fIci\fR and \fIco\fR operations were instrumented +to log the number of backward and forward deltas applied. +The data were collected during a 13 month period +from Dec. 1982 to Dec. 1983. +Table I summarizes the results. +.sp 0 +.nr VS 12p +.vs 12p +.TS +center,box,tab(#); +c|c|c|c|c s|c s +c|c|c|c|c s|c s +l|n|n|n|n n|n n. +Operation#Total#Total deltas#Mean deltas#Operations#Branch + #operations #applied#applied#with >1 delta#operations +_ +co # 7867# 9320#1.18#509#(6%)#203#(3%) +ci # 3468# 2207#0.64# 85#(2%)# 75#(2%) +ci & co#11335#11527#1.02#594#(5%)#278#(2%) +.TE +.ce 1 +Table I. Statistics for \fIco\fR and \fIci\fR operations. +.nr VS 18p +.vs 18p +.PP +The first two lines show statistics for check-out and check-in; +the third line shows the combination. +Recall that \fIci\fR performs an implicit check-out to obtain +a revision for computing the delta. +In all measures presented, the most recent revision (stored intact) +counts as one delta. The number of deltas applied represents +the number of passes necessary, where the first `pass' is a copying step. +.PP +Note that the check-out operation is executed more than +twice as frequently as the check-in operation. +The fourth column gives the mean number of deltas +applied in all three cases. +For \fIci\fR, the mean number of deltas applied is less +than one. +The reasons are that the initial check-in requires no delta at all, and that +the only time \fIci\fR requires more than one delta is for branches. +Column 5 shows the actual number of operations that applied more than one +delta. +The last column indicates that branches were not used often. +.PP +The last three columns demonstrate that the most recent trunk revision +is by far the most frequently accessed. +For RCS, check-out of +this revision is a simple copy operation, which is the absolute minimum +given the copy-semantics of \fIco\fR. +Access to older revisions and branches +is more common in non-academic environments, +yet even if access to older deltas were an order +of magnitude more frequent, +the combined average number of deltas applied would still be below 1.2. +Since RCS is faster than SCCS until up to 10 delta applications, +reverse deltas are clearly the method of choice. +.PP +The second experiment, conducted in March of 1984, +involved surveying the existing RCS files +on our two machines. The goal was to determine the mean number of +revisions per RCS file, as well as the space consumed by them. +Table II shows the results. (Tables I and II were produced at different +times and are unrelated.) +.sp 0 +.nr VS 12p +.vs 12p +.TS +center,box,tab(#); +c | c | c | c | c | c | c +c | c | c | c | c | c | c +l | n | n | n | n | n | n. + #Total RCS#Total#Mean#Mean size of#Mean size of#Overhead + #files#revisions#revisions#RCS files#revisions +_ +All files #8033#11133#1.39#6156#5585#1.10 +Files with#1477# 4578#3.10#8074#6041#1.34 +\(>= 2 deltas +.TE +.ce 1 +Table II. Statistics for RCS files. +.nr VS 18p +.vs 18p +.PP +The mean number of revisions per RCS file is 1.39. +Columns 5 and 6 show the mean sizes (in bytes) of an RCS file +and of the latest revision of each RCS file, respectively. +The `overhead' column contains the ratio of the mean sizes. +Assuming that all revisions in an RCS file are approximately the same size, +this ratio gives a measure of the space consumed by the extra revisions. +.PP +In our sample, over 80 per cent of the RCS files contained only a single revision. +The reason is that our +systems programmers routinely check in all source files +on the distribution tapes, even though they may never touch them again. +To get a better indication of how much space savings are possible +with deltas, all measures with those files +that contained 2 or more revisions were recomputed. Only for those files +is RCS necessary. +As shown in the second line, the average number of revisions for those files is +3.10, with an overhead of 1.34. This means that the extra 2.10 deltas +require 34 per cent extra space, or +16 per cent per extra revision. +Rochkind\u3\d +measured the space consumed by SCCS, and +reported an average of 5 revisions per group +and an overhead of 1.37 (or about 9 per cent per extra revision). +In a later paper, Glasser\u6\d +observed an average of 7 revisions per group in a single, large project, +but provided no overhead figure. +In his paper on DSEE\u5\d, +Leblang reported that delta storage combined with blank compression +results in an overhead of a mere 1\-2 per cent per revision. +Since leading blanks accounted for about 20 per cent of the surveyed Pascal +programs, a revision group with 5\-10 members was smaller +than a single cleartext copy. +.PP +The above observations demonstrate clearly that the space needed +for extra revisions is small. With delta storage, the luxury of +keeping multiple revisions online is certainly affordable. +In fact, introducing a system with delta storage may reduce +storage requirements, because programmers often save back-up copies +anyway. Since back-up copies are stored much more efficiently with deltas, +introducing a system such as RCS may +actually free a considerable amount of space. +.NH +Survey of Version Control Tools +.PP +The need to keep back-up copies of software arose when +programs and data were no longer stored on paper media, but were entered +from terminals and stored on disk. +Back-up copies are desirable for reliability, and many modern editors +automatically save a back-up copy for every file touched. +This strategy +is valuable for short-term back-ups, but not suitable for long-term +version control, since an existing back-up copy is overwritten whenever the +corresponding file is edited. +.PP +Tape archives are suitable for long-term, offline storage. +If all changed files are dumped on a back-up tape once per day, old revisions +remain accessible. However, tape archives are unsatisfactory +for version control in several ways. First, backing up the file +system every 24 hours does not capture intermediate revisions. +Secondly, the old revisions are not online, +and accessing them is tedious and time-consuming. +In particular, it is impractical to +compare several old revisions of a group, +because that may require mounting and searching several tapes. +Tape archives are important fail-safe tools in the +event of catastrophic disk failures or accidental deletions, +but they are ill-suited for version control. +Conversely, version control tools do not obviate the +need for tape archives. +.PP +A natural technique for keeping several old revisions online is +to never delete a file. +Editing a file +simply creates a new file with the same +name, but with a different sequence number. +This technique, available as an option in DEC's VMS operating system, +turns out to be inadequate for version control. +First, it is prohibitively expensive in terms of storage costs, +especially since no data compression techniques are employed. +Secondly, indiscriminately storing every change produces too many +revisions, and programmers have difficulties distinguishing them. +The proliferation of revisions forces programmers to spend much time on +finding and deleting useless files. +Thirdly, most of the support functions like locking, logging, +revision selection, +and identification described in this paper are not available. +.PP +An alternative approach is to separate editing from revision control. +The user may repeatedly edit a given revision, +until freezing it with an explicit command. +Once a revision is frozen, it is stored permanently and can no longer be modified. +(In RCS, freezing a revisions is done with \fIci\fR.) +Editing a frozen revision implicitly creates a new one, which +can again be changed repeatedly until it is frozen itself. +This approach saves exactly those revisions that the user +considers important, and keeps the number of revisions manageable. +IBM's CLEAR/CASTER\u7\d, +AT&T's SCCS\u3\d, +CMU's SDC\u8\d +and DEC's CMS\u9\d, +are examples of version control systems using this approach. +CLEAR/CASTER maintains a data base of programs, specifications, +documentation and messages, using deltas. +Its goal is to provide control over the development process from a +management viewpoint. +SCCS stores multiple revisions of source text in an ancestral tree, +records a log entry for each revision, +provides access control, and has facilities +for uniquely identifying each revision. +An efficient delta technique +reduces the space consumed by each revision group. +SDC is much simpler than SCCS because it stores not more than +two revisions. However, it maintains a complete log for all old +revisions, some of which may be on back-up tape. +CMS, like SCCS, manages tree-structured revision groups, +but offers no identification mechanism. +.PP +Tools for dealing with configurations are still in a state of flux. +SCCS, SDC and CMS can be combined with MAKE or MAKE-like programs. +Since flexible selection rules are missing from all these tools, +it is sometimes difficult +to specify precisely which revision of each group +should be passed to MAKE for building a desired configuration. +The Xerox Cedar system\u10\d +provides a `System Modeller' that can rebuild +a configuration from an arbitrary set of module revisions. +The revisions of a module are only distinguished by creation time, +and there is no tool for managing groups. +Since the selection rules are primitive, +the System Modeller appears to be somewhat tedious to use. +Apollo's DSEE\u5\d +is a sophisticated software engineering environment. +It manages revision groups in a way similar to SCCS and CMS. Configurations +are built using `configuration threads'. +A configuration thread states which revision of each group +named in a configuration should be chosen. +A configuration thread may contain dynamic specifiers +(e.g., `choose the revisions I am currently working on, +and the most recent revisions otherwise'), which are bound +automatically at build time. +It also provides a notification mechanism for alerting +maintainers about the need to rebuild a system after a change. +.PP +RCS is based on a general model for describing +multi-version/multi-configuration systems\u11\d. +The model describes systems using AND/OR graphs, where AND nodes represent +configurations, and OR nodes represent version groups. +The model gives rise to a suit of selection rules for +composing configurations, almost all of which are implemented in RCS. +The revisions selected by RCS are passed to MAKE for configuration building. +Revision group management is modelled after SCCS. +RCS retains SCCS's best features, +but offers a significantly simpler user interface, +flexible selection rules, adequate integration with MAKE +and improved identification. +A detailed comparison of RCS and SCCS appears in Reference 4. +.PP +An important component of all revision control systems +is a program for computing deltas. +SCCS and RCS use the program \fIdiff\fR\u2\d, +which first computes the longest common substring of two +revisions, and then produces the delta from that substring. +The delta is simply an edit script consisting of deletion and +insertion commands that generate one revision from the other. +.PP +A delta based on a longest common substring is not necessarily minimal, +because it does not take advantage of crossing block moves. +Crossing block moves arise if two or more blocks of lines +(e.g., procedures) +appear in a different order in two revisions. +An edit script derived from a longest common substring +first deletes the shorter of the two blocks, and then reinserts it. +Heckel\u12\d +proposed an algorithm for detecting block moves, but +since the algorithm is based on heuristics, +there are conditions +under which the generated delta is far from minimal. +DSEE uses this algorithm combined with blank compression, +apparently with satisfactory overall results. +A new algorithm that is guaranteed to produce a minimal delta based on +block moves appears in Reference 13. +A future release of RCS will use this algorithm. +.PP +\fIAcknowledgements\fR: +Many people have helped make RCS a success by contributed criticisms, suggestions, +corrections, and even whole new commands (including manual pages). +The list of people is too long to be +reproduced here, but my sincere thanks for their help and +goodwill goes to all of them. +.sp +.nr VS 12p +.vs 12p +.SH +Appendix: Synopsis of RCS Operations +.LP +.IP "\fIci\fP \fB\- check in revisions\fP" +.sp 0 +\fICi\fR stores the contents of a working file into the +corresponding RCS file as a new revision. +If the RCS file doesn't exist, \fIci\fR creates it. +\fICi\fR removes the working file, unless one of the options +\fI\-u\fR or \fI\-l\fR is present. +For each check-in, \fIci\fR asks for a commentary +describing the changes relative to the previous revision. +.sp 1 +\fICi\fR assigns the revision number given by the \fI\-r\fR option; +if that option is missing, it derives the number from the +lock held by the user; if there is no lock and locking is not strict, +\fIci\fR increments the number of the latest revision on the trunk. +A side branch can only be started by explicitly specifying its +number with the \fI\-r\fR option during check-in. +.sp 1 +\fICi\fR also determines +whether the revision to be checked in is different from the +previous one, and asks whether to proceed if not. +This facility simplifies check-in operations for large systems, +because one need not remember which files were changed. +.sp 1 +The option \fI\-k\fR searches the checked in file for identification +markers containing +the attributes +revision number, check-in date, author and state, and assigns these +to the new revision rather than computing them. This option is +useful for software distribution: Recipients of distributed software +using RCS should check in updates with the \fI\-k\fR option. +This convention guarantees that revision numbers, check-in dates, +etc., are the same at all sites. +.IP "\fIco\fP \fB\- check out revisions\fP" +.sp 0 +\fICo\fR retrieves revisions according to revision number, +date, author and state attributes. It either places the revision +into the working file, or prints it on the standard output. +\fICo\fR always expands the identification markers. +.IP "\fIident\fP \fB\- extract identification markers\fP" +.sp 0 +\fIIdent\fR extracts the identification markers expanded by \fIco\fR +from any file and prints them. +.IP "\fIrcs\fP \fB\- change RCS file attributes\fP" +.sp 0 +\fIRcs\fR is an administrative operation that changes access lists, +locks, unlocks, breaks locks, toggles the strict-locking feature, +sets state attributes and symbolic revision numbers, changes the +description, and deletes revisions. A revision can +only be deleted if it is not the fork of a side branch. +.br +.ne 10 +.IP "\fIrcsclean\fP \fB\- clean working directory\fP" +.sp 0 +\fIRcsclean\fR removes working files that were checked out but never changed.* +.FS * +The \fIrcsclean\fP and \fIrcsfreeze\fP commands +are optional and are not always installed. +.FE +.IP "\fIrcsdiff\fP \fB\- compare revisions\fP" +.sp 0 +\fIRcsdiff\fR compares two revisions and prints their +difference, using the UNIX tool \fIdiff\fR. +One of the revisions compared may be checked out. +This command is useful for finding out about changes. +.IP "\fIrcsfreeze\fP \fB\- freeze a configuration\fP" +.sp 0 +\fIRcsfreeze\fR assigns the same symbolic revision number +to a given revision in all RCS files. +This command is useful for accurately recording a configuration.* +.IP "\fIrcsmerge\fP \fB\- merge revisions\fP" +.sp 0 +\fIRcsmerge\fR merges two revisions, \fIrev1\fR and \fIrev2\fR, +with respect to a common ancestor. +A 3-way file comparison determines the segments of lines that +are (a) the same in all three revisions, or (b) the same in 2 revisions, +or (c) different in all three. For all segments of type (b) where +\fIrev1\fR is the differing revision, +the segment in \fIrev1\fR replaces the corresponding segment of \fIrev2\fR. +Type (c) indicates an overlapping change, is flagged as an error, and requires user +intervention to select the correct alternative. +.IP "\fIrlog\fP \fB\- read log messages\fP" +.sp 0 +\fIRlog\fR prints the log messages and other information in an RCS file. +.bp +.LP +.nr VS 12p +.vs 12p +.]< +.ds [F 1 +.]- +.ds [K FELD02 +.ds [K MakeArticle +.ds [A Feldman, Stuart I. +.ds [D March 1979 +.ds [T Make\*-A Program for Maintaining Computer Programs +.ds [J Software\*-Practice & Experience +.ds [V 9 +.ds [N 3 +.ds [P 255-265 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 2 +.]- +.ds [K HUNT01 +.ds [T An Algorithm for Differential File Comparison +.ds [A Hunt, James W. +.as [A " and McIlroy, M. D. +.ds [I Computing Science Technical Report, Bell Laboratories +.ds [R 41 +.ds [D June 1976 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 4 tech-report +.ds [F 3 +.]- +.ds [K SCCS +.ds [A Rochkind, Marc J. +.ds [D Dec. 1975 +.ds [T The Source Code Control System +.ds [J IEEE Transactions on Software Engineering +.ds [V SE-1 +.ds [N 4 +.ds [P 364-370 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 4 +.]- +.ds [K TICH08 +.ds [T Design, Implementation, and Evaluation of a Revision Control System +.ds [A Tichy, Walter F. +.ds [B Proceedings of the 6th International Conference on Software Engineering +.ds [I ACM, IEEE, IPS, NBS +.ds [D September 1982 +.ds [P 58-67 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 5 +.]- +.ds [K LEBL01 +.ds [A Leblang, David B. +.as [A " and Chase, Robert P. +.ds [T Computer-Aided Software Engineering in a Distributed Workstation Environment +.ds [O Proceedings of the ACM SIGSOFT/SIGPLAN Software Engineering Symposium +.as [O " on Practical Software Development Environments. +.ds [J SIGPLAN Notices +.ds [V 19 +.ds [N 5 +.ds [D May 1984 +.ds [P 104-112 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 1 +.ds [F 3 +.ds [F 6 +.]- +.ds [K SCCSEval +.ds [A Glasser, Alan L. +.ds [D Nov. 1978 +.ds [T The Evolution of a Source Code Control System +.ds [J Software Engineering Notes +.ds [V 3 +.ds [N 5 +.ds [P 122-125 +.nr [P 1 +.ds [O Proceedings of the Software Quality and Assurance Workshop. +.nr [T 0 +.nr [A 1 +.nr [O 1 +.][ 1 journal-article +.ds [F 5 +.ds [F 7 +.]- +.ds [K IBMClearCaster +.ds [A Brown, H.B. +.ds [D 1970 +.ds [T The Clear/Caster System +.ds [J Nato Conference on Software Engineering, Rome +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 3 +.ds [F 8 +.]- +.ds [K HabermannSDC +.ds [A Habermann, A. Nico +.ds [D Jan. 1979 +.ds [T A Software Development Control System +.ds [I Technical Report, Carnegie-Mellon University, Department of Computer Science +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 2 book +.ds [F 9 +.]- +.ds [K CMS +.ds [A DEC +.ds [T Code Management System +.ds [I Digital Equipment Corporation +.ds [O Document No.\ EA-23134-82 +.ds [D 1982 +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 2 book +.ds [F 10 +.]- +.ds [K LAMP01 +.ds [A Lampson, Butler W. +.as [A " and Schmidt, Eric E. +.ds [T Practical Use of a Polymorphic Applicative Language +.ds [B Proceedings of the 10th Symposium on Principles of Programming Languages +.ds [I ACM +.ds [P 237-255 +.nr [P 1 +.ds [D January 1983 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 5 +.ds [F 11 +.]- +.ds [K TICH07 +.ds [T A Data Model for Programming Support Environments and its Application +.ds [A Tichy, Walter F. +.ds [B Automated Tools for Information System Design and Development +.ds [E Hans-Jochen Schneider and Anthony I. Wasserman +.ds [C Amsterdam +.ds [I North-Holland Publishing Company +.ds [D 1982 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 4 +.ds [F 2 +.ds [F 12 +.]- +.ds [K HECK01 +.ds [T A Technique for Isolating Differences Between Files +.ds [A Heckel, Paul +.ds [J Communications of the ACM +.ds [D April 1978 +.ds [V 21 +.ds [N 4 +.ds [P 264-268 +.nr [P 1 +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 1 journal-article +.ds [F 13 +.]- +.ds [K TICH11 +.ds [T The String-to-String Correction Problem with Block Moves +.ds [A Tichy, Walter F. +.ds [D Nov. 1984 +.ds [J ACM Transactions on Computer Systems +.ds [V 2 +.ds [N 4 +.ds [P 309-321 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.]> diff --git a/gnu/usr.bin/rcs/doc/rcs_func.ms b/gnu/usr.bin/rcs/doc/rcs_func.ms new file mode 100644 index 00000000000..9818086c3de --- /dev/null +++ b/gnu/usr.bin/rcs/doc/rcs_func.ms @@ -0,0 +1,95 @@ +.SH +Functions of RCS (Revision Control System) +.PP +RCS manages software libraries. It greatly increases programmer productivity +by providing the following functions. +.IP 1. +RCS stores and retrieves multiple revisions of program and other text. +Thus, one can maintain one or more releases while developing the next +release, with a minimum of space overhead. Changes no longer destroy the +original -- previous revisions remain accessible. +.RS +.IP a. +Maintains each module as a tree of revisions. +.IP b. +Project libraries can +be organized centrally, decentralized, or any way you like. +.IP c. +RCS works for any type of text: programs, documentation, memos, papers, +graphics, VLSI layouts, form letters, etc. +.RE +.IP 2. +RCS maintains a complete history of changes. +Thus, one can find out what happened to a module easily +and quickly, without having to compare source listings or +having to track down colleagues. +.RS +.IP a. +RCS performs automatic record keeping. +.IP b. +RCS logs all changes automatically. +.IP c. +RCS guarantees project continuity. +.RE +.IP 3. +RCS manages multiple lines of development. +.IP 4. +RCS can merge multiple lines of development. +Thus, when several parallel lines of development must be consolidated +into one line, the merging of changes is automatic. +.IP 5. +RCS flags coding conflicts. +If two or more lines of development modify the same section of code, +RCS can alert programmers about overlapping changes. +.IP 6. +RCS resolves access conflicts. +When two or more programmers wish to modify the same revision, +RCS alerts the programmers and makes sure that one modification won't wipe +out the other one. +.IP 7. +RCS provides high-level retrieval functions. +Revisions can be retrieved according to ranges of revision numbers, +symbolic names, dates, authors, and states. +.IP 8. +RCS provides release and configuration control. +Revisions can be marked as released, stable, experimental, etc. +Configurations of modules can be described simply and directly. +.IP 9. +RCS performs automatic identification of modules with name, revision +number, creation time, author, etc. +Thus, it is always possible to determine which revisions of which +modules make up a given configuration. +.IP 10. +Provides high-level management visibility. +Thus, it is easy to track the status of a software project. +.RS +.IP a. +RCS provides a complete change history. +.IP b. +RCS records who did what when to which revision of which module. +.RE +.IP 11. +RCS is fully compatible with existing software development tools. +RCS is unobtrusive -- its interface to the file system is such that +all your existing software tools can be used as before. +.IP 12. +RCS' basic user interface is extremely simple. The novice need to learn +only two commands. Its more sophisticated features have been +tuned towards advanced software development environments and the +experienced software professional. +.IP 13. +RCS simplifies software distribution if customers +maintain sources with RCS also. This technique assures proper +identification of versions and configurations, and tracking of customer +modifications. Customer modifications can be merged into distributed +versions locally or by the development group. +.IP 14. +RCS needs little extra space for the revisions (only the differences). +If intermediate revisions are deleted, the corresponding +differences are compressed into the shortest possible form. +.IP 15. +RCS is implemented with reverse deltas. This means that +the latest revision, which is the one that is accessed most often, +is stored intact. All others are regenerated from the latest one +by applying reverse deltas (backward differences). This +results in fast access time for the revision needed most often. diff --git a/gnu/usr.bin/rcs/ident/Makefile b/gnu/usr.bin/rcs/ident/Makefile new file mode 100644 index 00000000000..f28f8d3c5b7 --- /dev/null +++ b/gnu/usr.bin/rcs/ident/Makefile @@ -0,0 +1,8 @@ +PROG= ident +SRCS= ident.c +CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/ident/ident.1 b/gnu/usr.bin/rcs/ident/ident.1 new file mode 100644 index 00000000000..253a2ce7272 --- /dev/null +++ b/gnu/usr.bin/rcs/ident/ident.1 @@ -0,0 +1,182 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $FreeBSD$ +.ds r \&\s-1RCS\s0 +.ds u \&\s-1UTC\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH IDENT 1 \*(Dt GNU +.SH NAME +ident \- identify RCS keyword strings in files +.SH SYNOPSIS +.B ident +[ +.B \-q +] [ +.B \-V +] [ +.I file +\&.\|.\|. ] +.SH DESCRIPTION +.B ident +searches for all instances of the pattern +.BI $ keyword : "\ text\ " $ +in the named files or, if no files are named, the standard input. +.PP +These patterns are normally inserted automatically by the \*r command +.BR co (1), +but can also be inserted manually. +The option +.B \-q +suppresses +the warning given if there are no patterns in a file. +The option +.B \-V +prints +.BR ident 's +version number. +.PP +.B ident +works on text files as well as object files and dumps. +For example, if the C program in +.B f.c +contains +.IP +.ft 3 +#include +.br +static char const rcsid[] = +.br + \&"$\&Id: f.c,v \*(iD $\&"; +.br +int main() { return printf(\&"%s\en\&", rcsid) == EOF; } +.ft P +.LP +and +.B f.c +is compiled into +.BR f.o , +then the command +.IP +.B "ident f.c f.o" +.LP +will output +.nf +.IP +.ft 3 +f.c: + $\&Id: f.c,v \*(iD $ +f.o: + $\&Id: f.c,v \*(iD $ +.ft +.fi +.PP +If a C program defines a string like +.B rcsid +above but does not use it, +.BR lint (1) +may complain, and some C compilers will optimize away the string. +The most reliable solution is to have the program use the +.B rcsid +string, as shown in the example above. +.PP +.B ident +finds all instances of the +.BI $ keyword : "\ text\ " $ +pattern, even if +.I keyword +is not actually an \*r-supported keyword. +This gives you information about nonstandard keywords like +.BR $\&XConsortium$ . +.SH KEYWORDS +Here is the list of keywords currently maintained by +.BR co (1). +All times are given in Coordinated Universal Time (\*u, +sometimes called \&\s-1GMT\s0) by default, but if the files +were checked out with +.BR co 's +.BI \-z zone +option, times are given with a numeric time zone indication appended. +.TP +.B $\&Author$ +The login name of the user who checked in the revision. +.TP +.B $\&Date$ +The date and time the revision was checked in. +.TP +.B $\&Header$ +A standard header containing the full pathname of the \*r file, the +revision number, the date and time, the author, the state, +and the locker (if locked). +.TP +.B $\&Id$ +Same as +.BR $\&Header$ , +except that the \*r filename is without a path. +.TP +.B $\&Locker$ +The login name of the user who locked the revision (empty if not locked). +.TP +.B $\&Log$ +The log message supplied during checkin. +For +.BR ident 's +purposes, this is equivalent to +.BR $\&RCSfile$ . +.TP +.B $\&Name$ +The symbolic name used to check out the revision, if any. +.TP +.B $\&RCSfile$ +The name of the \*r file without a path. +.TP +.B $\&Revision$ +The revision number assigned to the revision. +.TP +.B $\&Source$ +The full pathname of the \*r file. +.TP +.B $\&State$ +The state assigned to the revision with the +.B \-s +option of +.BR rcs (1) +or +.BR ci (1). +.PP +.BR co (1) +represents the following characters in keyword values by escape sequences +to keep keyword strings well-formed. +.LP +.RS +.nf +.ne 6 +.ta \w'newline 'u +\f2char escape sequence\fP +tab \f3\et\fP +newline \f3\en\fP +space \f3\e040 +$ \e044 +\e \e\e\fP +.fi +.RE +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1992, 1993 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/gnu/usr.bin/rcs/ident/ident.c b/gnu/usr.bin/rcs/ident/ident.c new file mode 100644 index 00000000000..9b8bf57365e --- /dev/null +++ b/gnu/usr.bin/rcs/ident/ident.c @@ -0,0 +1,270 @@ +/* Identify RCS keyword strings in files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.9 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.8 1995/06/01 16:23:43 eggert + * (exiterr, reportError): New functions, needed for DOS and OS/2 ports. + * (scanfile): Use them. + * + * Revision 5.7 1994/03/20 04:52:58 eggert + * Remove `exiting' from identExit. + * + * Revision 5.6 1993/11/09 17:40:15 eggert + * Add -V. + * + * Revision 5.5 1993/11/03 17:42:27 eggert + * Test for char == EOF, not char < 0. + * + * Revision 5.4 1992/01/24 18:44:19 eggert + * lint -> RCS_lint + * + * Revision 5.3 1991/09/10 22:15:46 eggert + * Open files with FOPEN_R, not FOPEN_R_WORK, + * because they might be executables, not working files. + * + * Revision 5.2 1991/08/19 03:13:55 eggert + * Report read errors immediately. + * + * Revision 5.1 1991/02/25 07:12:37 eggert + * Don't report empty keywords. Check for I/O errors. + * + * Revision 5.0 1990/08/22 08:12:37 eggert + * Don't limit output to known keywords. + * Remove arbitrary limits and lint. Ansify and Posixate. + * + * Revision 4.5 89/05/01 15:11:54 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 87/10/23 17:09:57 narten + * added exit(0) so exit return code would be non random + * + * Revision 4.3 87/10/18 10:23:55 narten + * Updating version numbers. Changes relative to 1.1 are actually relative + * to 4.1 + * + * Revision 1.3 87/07/09 09:20:52 trinkle + * Added check to make sure there is at least one arg before comparing argv[1] + * with "-q". This necessary on machines that don't allow dereferncing null + * pointers (i.e. Suns). + * + * Revision 1.2 87/03/27 14:21:47 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 16:31:02 wft + * Added option -q and input from reading stdin. + * Marker matching is now done with trymatch() (independent of keywords). + * + * Revision 3.4 83/02/18 17:37:49 wft + * removed printing of new line after last file. + * + * Revision 3.3 82/12/04 12:48:55 wft + * Added LOCKER. + * + * Revision 3.2 82/11/28 18:24:17 wft + * removed Suffix; added ungetc to avoid skipping over trailing KDELIM. + * + * Revision 3.1 82/10/13 15:58:51 wft + * fixed type of variables receiving from getc() (char-->int). +*/ + +#include "rcsbase.h" + +static int match P((FILE*)); +static int scanfile P((FILE*,char const*,int)); +static void reportError P((char const*)); + +mainProg(identId, "ident", "$FreeBSD$") +/* Ident searches the named files for all occurrences + * of the pattern $@: text $ where @ is a keyword. + */ + +{ + FILE *fp; + int quiet = 0; + int status = EXIT_SUCCESS; + char const *a; + + while ((a = *++argv) && *a=='-') + while (*++a) + switch (*a) { + case 'q': + quiet = 1; + break; + + case 'V': + VOID printf("RCS version %s\n", RCS_version_string); + quiet = -1; + break; + + default: + VOID fprintf(stderr, + "ident: usage: ident -{qV} [file...]\n" + ); + exitmain(EXIT_FAILURE); + break; + } + + if (0 <= quiet) + if (!a) + VOID scanfile(stdin, (char*)0, quiet); + else + do { + if (!(fp = fopen(a, FOPEN_RB))) { + reportError(a); + status = EXIT_FAILURE; + } else if ( + scanfile(fp, a, quiet) != 0 + || (argv[1] && putchar('\n') == EOF) + ) + break; + } while ((a = *++argv)); + + if (ferror(stdout) || fclose(stdout)!=0) { + reportError("standard output"); + status = EXIT_FAILURE; + } + exitmain(status); +} + +#if RCS_lint +# define exiterr identExit +#endif + void +exiterr() +{ + _exit(EXIT_FAILURE); +} + + static void +reportError(s) + char const *s; +{ + int e = errno; + VOID fprintf(stderr, "%s error: ", cmdid); + errno = e; + perror(s); +} + + + static int +scanfile(file, name, quiet) + register FILE *file; + char const *name; + int quiet; +/* Function: scan an open file with descriptor file for keywords. + * Return -1 if there's a write error; exit immediately on a read error. + */ +{ + register int c; + + if (name) { + VOID printf("%s:\n", name); + if (ferror(stdout)) + return -1; + } else + name = "standard input"; + c = 0; + while (c != EOF || ! (feof(file)|ferror(file))) { + if (c == KDELIM) { + if ((c = match(file))) + continue; + if (ferror(stdout)) + return -1; + quiet = true; + } + c = getc(file); + } + if (ferror(file) || fclose(file) != 0) { + reportError(name); + /* + * The following is equivalent to exit(EXIT_FAILURE), but we invoke + * exiterr to keep lint happy. The DOS and OS/2 ports need exiterr. + */ + VOID fflush(stderr); + VOID fflush(stdout); + exiterr(); + } + if (!quiet) + VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, name); + return 0; +} + + + + static int +match(fp) /* group substring between two KDELIM's; then do pattern match */ + register FILE *fp; +{ + char line[BUFSIZ]; + register int c; + register char * tp; + + tp = line; + while ((c = getc(fp)) != VDELIM) { + if (c == EOF && feof(fp) | ferror(fp)) + return c; + switch (ctab[c]) { + case LETTER: case Letter: case DIGIT: + *tp++ = c; + if (tp < line+sizeof(line)-4) + break; + /* fall into */ + default: + return c ? c : '\n'/* anything but 0 or KDELIM or EOF */; + } + } + if (tp == line) + return c; + *tp++ = c; + if ((c = getc(fp)) != ' ') + return c ? c : '\n'; + *tp++ = c; + while( (c = getc(fp)) != KDELIM ) { + if (c == EOF && feof(fp) | ferror(fp)) + return c; + switch (ctab[c]) { + default: + *tp++ = c; + if (tp < line+sizeof(line)-2) + break; + /* fall into */ + case NEWLN: case UNKN: + return c ? c : '\n'; + } + } + if (tp[-1] != ' ') + return c; + *tp++ = c; /*append trailing KDELIM*/ + *tp = '\0'; + VOID printf(" %c%s\n", KDELIM, line); + return 0; +} diff --git a/gnu/usr.bin/rcs/lib/Makefile b/gnu/usr.bin/rcs/lib/Makefile new file mode 100644 index 00000000000..f21fda1e810 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + +# Define FSYNC_ALL to get slower but safer writes in case of crashes in +# the middle of CVS/RCS changes +#CFLAGS += -DFSYNC_ALL + +LIB = rcs +SRCS = maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \ + rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcstime.c \ + rcsutil.c merger.c version.c + +INTERNALLIB= + +.include diff --git a/gnu/usr.bin/rcs/lib/conf.h b/gnu/usr.bin/rcs/lib/conf.h new file mode 100644 index 00000000000..96ec07d6c87 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/conf.h @@ -0,0 +1,400 @@ +/* RCS compile-time configuration */ + + /* $FreeBSD$ */ + +/* + * This file is generated automatically. + * If you edit it by hand your changes may be lost. + * Instead, please try to fix conf.sh, + * and send your fixes to rcs-bugs@cs.purdue.edu. + */ + +#define exitmain(n) return n /* how to exit from main() */ +/* #define _POSIX_C_SOURCE 2147483647L */ /* if strict C + Posix 1003.1b-1993 or later */ +/* #define _POSIX_SOURCE */ /* if strict C + Posix 1003.1-1990 */ + +#include +#include +#include + +/* Comment out #include lines below that do not work. */ +#include +#include +#include +#include +#include +/* #include */ +/* #include */ +#include +/* #include */ +#include +#include +#include +#include +#include +/* #include */ +#include +#include +/* #include */ + +/* Define boolean symbols to be 0 (false, the default), or 1 (true). */ +#define has_sys_param_h 1 /* Does #include work? */ +/* extern int errno; */ /* Uncomment if doesn't declare errno. */ +#define has_readlink 1 /* Does readlink() work? */ +#define readlink_isreg_errno EINVAL /* errno after readlink on regular file */ + +#if has_readlink && !defined(MAXSYMLINKS) +# if has_sys_param_h +# include +# endif +# ifndef MAXSYMLINKS +# define MAXSYMLINKS 20 /* BSD; not standard yet */ +# endif +#endif + +/* Comment out the typedefs below if the types are already declared. */ +/* Fix any uncommented typedefs that are wrong. */ +/* typedef int mode_t; */ +/* typedef long off_t; */ +/* typedef int pid_t; */ +/* typedef int sig_atomic_t; */ +/* typedef unsigned size_t; */ +/* typedef int ssize_t; */ +/* typedef long time_t; */ +/* typedef int uid_t; */ + +/* Comment out the keyword definitions below if the keywords work. */ +/* #define const */ +/* #define volatile */ + +/* Define boolean symbols to be 0 (false, the default), or 1 (true). */ +#define has_prototypes 1 /* Do function prototypes work? */ +#define has_stdarg 1 /* Does work? */ +/* #define has_varargs ? */ /* Does work? */ +#define va_start_args 2 /* How many args does va_start() take? */ + +#if O_BINARY + /* Text and binary i/o behave differently. */ + /* This is incompatible with Posix and Unix. */ +# define FOPEN_RB "rb" +# define FOPEN_R_WORK (Expand==BINARY_EXPAND ? "r" : "rb") +# define FOPEN_WB "wb" +# define FOPEN_W_WORK (Expand==BINARY_EXPAND ? "w" : "wb") +# define FOPEN_WPLUS_WORK (Expand==BINARY_EXPAND ? "w+" : "w+b") +# define OPEN_O_BINARY O_BINARY +#else + /* + * Text and binary i/o behave the same. + * Omit "b", since some nonstandard hosts reject it. + */ +# define FOPEN_RB "r" +# define FOPEN_R_WORK "r" +# define FOPEN_WB "w" +# define FOPEN_W_WORK "w" +# define FOPEN_WPLUS_WORK "w+" +# define OPEN_O_BINARY 0 +#endif + +/* This may need changing on non-Unix systems (notably DOS). */ +#define OPEN_CREAT_READONLY (S_IRUSR|S_IRGRP|S_IROTH) /* lock file mode */ +#define OPEN_O_LOCK 0 /* extra open flags for creating lock file */ +#define OPEN_O_WRONLY O_WRONLY /* main open flag for creating a lock file */ + +/* Define or comment out the following symbols as needed. */ +#if has_prototypes +# define P(params) params +#else +# define P(params) () +#endif +#if has_stdarg +# include +#else +# if has_varargs +# include +# else + typedef char *va_list; +# define va_dcl int va_alist; +# define va_start(ap) ((ap) = (va_list)&va_alist) +# define va_arg(ap,t) (((t*) ((ap)+=sizeof(t))) [-1]) +# define va_end(ap) +# endif +#endif +#if va_start_args == 2 +# define vararg_start va_start +#else +# define vararg_start(ap,p) va_start(ap) +#endif +#define bad_chmod_close 0 /* Can chmod() close file descriptors? */ +#define bad_creat0 0 /* Do writes fail after creat(f,0)? */ +#define bad_fopen_wplus 0 /* Does fopen(f,"w+") fail to truncate f? */ +#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */ +#define has_attribute_noreturn 1 /* Does __attribute__((noreturn)) work? */ +#if has_attribute_noreturn +# define exiting __attribute__((noreturn)) +#else +# define exiting +#endif +#define has_dirent 1 /* Do opendir(), readdir(), closedir() work? */ +#define void_closedir 0 /* Does closedir() yield void? */ +#define has_fchmod 1 /* Does fchmod() work? */ +#define has_fflush_input 0 /* Does fflush() work on input files? */ +#define has_fputs 1 /* Does fputs() work? */ +#define has_ftruncate 1 /* Does ftruncate() work? */ +#define has_getuid 1 /* Does getuid() work? */ +#define has_getpwuid 1 /* Does getpwuid() work? */ +#define has_memcmp 1 /* Does memcmp() work? */ +#define has_memcpy 1 /* Does memcpy() work? */ +#define has_memmove 1 /* Does memmove() work? */ +#define has_map_fd 0 /* Does map_fd() work? */ +#define has_mmap 1 /* Does mmap() work on regular files? */ +#define has_madvise 0 /* Does madvise() work? */ +#define mmap_signal SIGBUS /* signal received if you reference nonexistent part of mmapped file */ +#define has_rename 1 /* Does rename() work? */ +#define bad_a_rename 0 /* Does rename(A,B) fail if A is unwritable? */ +#define bad_b_rename 0 /* Does rename(A,B) fail if B is unwritable? */ +#define bad_NFS_rename 0 /* Can rename(A,B) falsely report success? */ +/* typedef int void; */ /* Some ancient compilers need this. */ +#define VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */ +#define has_seteuid 1 /* Does seteuid() work? See ../INSTALL.RCS. */ +#define has_setreuid 0 /* Does setreuid() work? See ../INSTALL.RCS. */ +#define has_setuid 1 /* Does setuid() exist? */ +#define has_sigaction 1 /* Does struct sigaction work? */ +#define has_sa_sigaction 1 /* Does struct sigaction have sa_sigaction? */ +#define has_signal 1 /* Does signal() work? */ +#define signal_type void /* type returned by signal handlers */ +#define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */ +/* #define has_sigblock ? */ /* Does sigblock() work? */ +/* #define sigmask(s) (1 << ((s)-1)) */ /* Yield mask for signal number. */ +typedef size_t fread_type; /* type returned by fread() and fwrite() */ +typedef size_t freadarg_type; /* type of their size arguments */ +typedef void *malloc_type; /* type returned by malloc() */ +#define has_getcwd 1 /* Does getcwd() work? */ +/* #define has_getwd ? */ /* Does getwd() work? */ +#define needs_getabsname 0 /* Must we define getabsname? */ +#define has_mktemp 1 /* Does mktemp() work? */ +#define has_mkstemp 1 /* Does mkstemp() work? */ +#define has_NFS 1 /* Might NFS be used? */ +#define has_psiginfo 0 /* Does psiginfo() work? */ +#define has_psignal 1 /* Does psignal() work? */ +/* #define has_si_errno ? */ /* Does siginfo_t have si_errno? */ +/* #define has_sys_siglist ? */ /* Does sys_siglist[] work? */ +/* #define strchr index */ /* Use old-fashioned name for strchr()? */ +/* #define strrchr rindex */ /* Use old-fashioned name for strrchr()? */ +#define bad_unlink 0 /* Does unlink() fail on unwritable files? */ +#define has_vfork 1 /* Does vfork() work? */ +#define has_fork 1 /* Does fork() work? */ +#define has_spawn 0 /* Does spawn*() work? */ +#define has_waitpid 1 /* Does waitpid() work? */ +#define bad_wait_if_SIGCHLD_ignored 0 /* Does ignoring SIGCHLD break wait()? */ +#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */ +#define has_printf_dot 1 /* Does "%.2d" print leading 0? */ +#define has_vfprintf 1 /* Does vfprintf() work? */ +#define has_attribute_format_printf 1 /* Does __attribute__((format(printf,N,N+1))) work? */ +#if has_attribute_format_printf +# define printf_string(m, n) __attribute__((format(printf, m, n))) +#else +# define printf_string(m, n) +#endif +#if has_attribute_format_printf && has_attribute_noreturn + /* Work around a bug in GCC 2.5.x. */ +# define printf_string_exiting(m, n) __attribute__((format(printf, m, n), noreturn)) +#else +# define printf_string_exiting(m, n) printf_string(m, n) exiting +#endif +/* #define has__doprintf ? */ /* Does _doprintf() work? */ +/* #define has__doprnt ? */ /* Does _doprnt() work? */ +/* #undef EXIT_FAILURE */ /* Uncomment this if EXIT_FAILURE is broken. */ +#define large_memory 1 /* Can main memory hold entire RCS files? */ +#ifndef LONG_MAX +#define LONG_MAX 2147483647L /* long maximum */ +#endif +/* Do struct stat s and t describe the same file? Answer d if unknown. */ +#define same_file(s,t,d) ((s).st_ino==(t).st_ino && (s).st_dev==(t).st_dev) +#define has_utimbuf 1 /* Does struct utimbuf work? */ +#define CO "/usr/bin/co" /* name of 'co' program */ +#define COMPAT2 0 /* Are version 2 files supported? */ +#define DIFF "/usr/bin/diff" /* name of 'diff' program */ +#define DIFF3 "/usr/bin/diff3" /* name of 'diff3' program */ +#define DIFF3_BIN 1 /* Is diff3 user-visible (not the /usr/lib auxiliary)? */ +#define DIFFFLAGS "-an" /* Make diff output suitable for RCS. */ +#define DIFF_L 1 /* Does diff -L work? */ +#define DIFF_SUCCESS 0 /* DIFF status if no differences are found */ +#define DIFF_FAILURE 1 /* DIFF status if differences are found */ +#define DIFF_TROUBLE 2 /* DIFF status if trouble */ +#define ED "/bin/ed" /* name of 'ed' program (used only if !DIFF3_BIN) */ +#define MERGE "/usr/bin/merge" /* name of 'merge' program */ +#define TMPDIR "/tmp" /* default directory for temporary files */ +#define SLASH '/' /* principal filename separator */ +#define SLASHes '/' /* `case SLASHes:' labels all filename separators */ +#define isSLASH(c) ((c) == SLASH) /* Is arg a filename separator? */ +#define ROOTPATH(p) isSLASH((p)[0]) /* Is p an absolute pathname? */ +#define X_DEFAULT ",v/" /* default value for -x option */ +#define SLASHSLASH_is_SLASH 1 /* Are // and / the same directory? */ +#define ALL_ABSOLUTE 1 /* Do all subprograms satisfy ROOTPATH? */ +#define DIFF_ABSOLUTE 1 /* Is ROOTPATH(DIFF) true? */ +#define SENDMAIL "/usr/sbin/sendmail" /* how to send mail */ +#define TZ_must_be_set 0 /* Must TZ be set for gmtime() to work? */ + + + +/* Adjust the following declarations as needed. */ + + +/* The rest is for the benefit of non-standard, traditional hosts. */ +/* Don't bother to declare functions that in traditional hosts do not appear, */ +/* or are declared in .h files, or return int or void. */ + + +/* traditional BSD */ + +#if has_sys_siglist && !defined(sys_siglist) + extern char const * const sys_siglist[]; +#endif + + +/* Posix (ISO/IEC 9945-1: 1990 / IEEE Std 1003.1-1990) */ + +/* */ +#ifdef O_CREAT +# define open_can_creat 1 +#else +# define open_can_creat 0 +# define O_RDONLY 0 +# define O_WRONLY 1 +# define O_RDWR 2 +# define O_CREAT 01000 +# define O_TRUNC 02000 +#endif +#ifndef O_EXCL +#define O_EXCL 0 +#endif + +/* */ +#ifndef S_IRUSR +# ifdef S_IREAD +# define S_IRUSR S_IREAD +# else +# define S_IRUSR 0400 +# endif +# ifdef S_IWRITE +# define S_IWUSR S_IWRITE +# else +# define S_IWUSR (S_IRUSR/2) +# endif +#endif +#ifndef S_IRGRP +# if has_getuid +# define S_IRGRP (S_IRUSR / 0010) +# define S_IWGRP (S_IWUSR / 0010) +# define S_IROTH (S_IRUSR / 0100) +# define S_IWOTH (S_IWUSR / 0100) +# else + /* single user OS -- not Posix or Unix */ +# define S_IRGRP 0 +# define S_IWGRP 0 +# define S_IROTH 0 +# define S_IWOTH 0 +# endif +#endif +#ifndef S_ISREG +#define S_ISREG(n) (((n) & S_IFMT) == S_IFREG) +#endif + +/* */ +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */ +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 0377) == 0) +#endif +#ifndef WTERMSIG +#define WTERMSIG(stat_val) ((stat_val) & 0177) +#undef WIFSIGNALED /* Avoid 4.3BSD incompatibility with Posix. */ +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(stat_val) ((unsigned)(stat_val) - 1 < 0377) +#endif + +/* */ +char *getlogin P((void)); +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 +#endif +#if has_fork && !has_vfork +# undef vfork +# define vfork fork +#endif +#if has_getcwd || !has_getwd + char *getcwd P((char*,size_t)); +#else + char *getwd P((char*)); +#endif +#if has_setuid && !has_seteuid +# undef seteuid +# define seteuid setuid +#endif +#if has_spawn +# if ALL_ABSOLUTE +# define spawn_RCS spawnv +# else +# define spawn_RCS spawnvp +# endif +#else +# if ALL_ABSOLUTE +# define exec_RCS execv +# else +# define exec_RCS execvp +# endif +#endif + +/* utime.h */ +#if !has_utimbuf + struct utimbuf { time_t actime, modtime; }; +#endif + + +/* Standard C library */ + +/* */ +#ifndef L_tmpnam +#define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */ +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif +#if has_mktemp + char *mktemp P((char*)); /* traditional */ +#else + char *tmpnam P((char*)); +#endif + +/* */ +char *getenv P((char const*)); +void _exit P((int)) exiting; +void exit P((int)) exiting; +malloc_type malloc P((size_t)); +malloc_type realloc P((malloc_type,size_t)); +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#endif +#ifndef EXIT_SUCCESS +#define EXIT_SUCCESS 0 +#endif + +/* */ +char *strcpy P((char*,char const*)); +char *strchr P((char const*,int)); +char *strrchr P((char const*,int)); +void *memcpy P((void*,void const*,size_t)); +#if has_memmove + void *memmove P((void*,void const*,size_t)); +#endif + +/* */ +time_t time P((time_t*)); diff --git a/gnu/usr.bin/rcs/lib/maketime.c b/gnu/usr.bin/rcs/lib/maketime.c new file mode 100644 index 00000000000..0f83bf5690d --- /dev/null +++ b/gnu/usr.bin/rcs/lib/maketime.c @@ -0,0 +1,344 @@ +/* Convert struct partime into time_t. */ + +/* Copyright 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#if has_conf_h +# include "conf.h" +#else +# ifdef __STDC__ +# define P(x) x +# else +# define const +# define P(x) () +# endif +# include +# include +#endif + +#include "partime.h" +#include "maketime.h" + +char const maketId[] + = "$FreeBSD$"; + +static int isleap P((int)); +static int month_days P((struct tm const*)); +static time_t maketime P((struct partime const*,time_t)); + +/* +* For maximum portability, use only localtime and gmtime. +* Make no assumptions about the time_t epoch or the range of time_t values. +* Avoid mktime because it's not universal and because there's no easy, +* portable way for mktime to yield the inverse of gmtime. +*/ + +#define TM_YEAR_ORIGIN 1900 + + static int +isleap(y) + int y; +{ + return (y&3) == 0 && (y%100 != 0 || y%400 == 0); +} + +static int const month_yday[] = { + /* days in year before start of months 0-12 */ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 +}; + +/* Yield the number of days in TM's month. */ + static int +month_days(tm) + struct tm const *tm; +{ + int m = tm->tm_mon; + return month_yday[m+1] - month_yday[m] + + (m==1 && isleap(tm->tm_year + TM_YEAR_ORIGIN)); +} + +/* +* Convert UNIXTIME to struct tm form. +* Use gmtime if available and if !LOCALZONE, localtime otherwise. +*/ + struct tm * +time2tm(unixtime, localzone) + time_t unixtime; + int localzone; +{ + struct tm *tm; +# if TZ_must_be_set + static char const *TZ; + if (!TZ && !(TZ = getenv("TZ"))) + faterror("The TZ environment variable is not set; please set it to your timezone"); +# endif + if (localzone || !(tm = gmtime(&unixtime))) + tm = localtime(&unixtime); + return tm; +} + +/* Yield A - B, measured in seconds. */ + time_t +difftm(a, b) + struct tm const *a, *b; +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + int difference_in_day_of_year = a->tm_yday - b->tm_yday; + int intervening_leap_days = ( + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + ); + time_t difference_in_years = ay - by; + time_t difference_in_days = ( + difference_in_years*365 + + (intervening_leap_days + difference_in_day_of_year) + ); + return + ( + ( + 24*difference_in_days + + (a->tm_hour - b->tm_hour) + )*60 + (a->tm_min - b->tm_min) + )*60 + (a->tm_sec - b->tm_sec); +} + +/* +* Adjust time T by adding SECONDS. SECONDS must be at most 24 hours' worth. +* Adjust only T's year, mon, mday, hour, min and sec members; +* plus adjust wday if it is defined. +*/ + void +adjzone(t, seconds) + register struct tm *t; + long seconds; +{ + /* + * This code can be off by a second if SECONDS is not a multiple of 60, + * if T is local time, and if a leap second happens during this minute. + * But this bug has never occurred, and most likely will not ever occur. + * Liberia, the last country for which SECONDS % 60 was nonzero, + * switched to UTC in May 1972; the first leap second was in June 1972. + */ + int leap_second = t->tm_sec == 60; + long sec = seconds + (t->tm_sec - leap_second); + if (sec < 0) { + if ((t->tm_min -= (59-sec)/60) < 0) { + if ((t->tm_hour -= (59-t->tm_min)/60) < 0) { + t->tm_hour += 24; + if (TM_DEFINED(t->tm_wday) && --t->tm_wday < 0) + t->tm_wday = 6; + if (--t->tm_mday <= 0) { + if (--t->tm_mon < 0) { + --t->tm_year; + t->tm_mon = 11; + } + t->tm_mday = month_days(t); + } + } + t->tm_min += 24 * 60; + } + sec += 24L * 60 * 60; + } else + if (60 <= (t->tm_min += sec/60)) + if (24 <= (t->tm_hour += t->tm_min/60)) { + t->tm_hour -= 24; + if (TM_DEFINED(t->tm_wday) && ++t->tm_wday == 7) + t->tm_wday = 0; + if (month_days(t) < ++t->tm_mday) { + if (11 < ++t->tm_mon) { + ++t->tm_year; + t->tm_mon = 0; + } + t->tm_mday = 1; + } + } + t->tm_min %= 60; + t->tm_sec = (int) (sec%60) + leap_second; +} + +/* +* Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise. +* Use only TM's year, mon, mday, hour, min, and sec members. +* Ignore TM's old tm_yday and tm_wday, but fill in their correct values. +* Yield -1 on failure (e.g. a member out of range). +* Posix 1003.1-1990 doesn't allow leap seconds, but some implementations +* have them anyway, so allow them if localtime/gmtime does. +*/ + time_t +tm2time(tm, localzone) + struct tm *tm; + int localzone; +{ + /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */ + static time_t t_cache[2]; + static struct tm tm_cache[2]; + + time_t d, gt; + struct tm const *gtm; + /* + * The maximum number of iterations should be enough to handle any + * combinations of leap seconds, time zone rule changes, and solar time. + * 4 is probably enough; we use a bigger number just to be safe. + */ + int remaining_tries = 8; + + /* Avoid subscript errors. */ + if (12 <= (unsigned)tm->tm_mon) + return -1; + + tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday + - (tm->tm_mon<2 || ! isleap(tm->tm_year + TM_YEAR_ORIGIN)); + + /* Make a first guess. */ + gt = t_cache[localzone]; + gtm = gt ? &tm_cache[localzone] : time2tm(gt,localzone); + + /* Repeatedly use the error from the guess to improve the guess. */ + while ((d = difftm(tm, gtm)) != 0) { + if (--remaining_tries == 0) + return -1; + gt += d; + gtm = time2tm(gt,localzone); + } + t_cache[localzone] = gt; + tm_cache[localzone] = *gtm; + + /* + * Check that the guess actually matches; + * overflow can cause difftm to yield 0 even on differing times, + * or tm may have members out of range (e.g. bad leap seconds). + */ + if ( (tm->tm_year ^ gtm->tm_year) + | (tm->tm_mon ^ gtm->tm_mon) + | (tm->tm_mday ^ gtm->tm_mday) + | (tm->tm_hour ^ gtm->tm_hour) + | (tm->tm_min ^ gtm->tm_min) + | (tm->tm_sec ^ gtm->tm_sec)) + return -1; + + tm->tm_wday = gtm->tm_wday; + return gt; +} + +/* +* Check *PT and convert it to time_t. +* If it is incompletely specified, use DEFAULT_TIME to fill it out. +* Use localtime if PT->zone is the special value TM_LOCAL_ZONE. +* Yield -1 on failure. +* ISO 8601 day-of-year and week numbers are not yet supported. +*/ + static time_t +maketime(pt, default_time) + struct partime const *pt; + time_t default_time; +{ + int localzone, wday; + struct tm tm; + struct tm *tm0 = 0; + time_t r; + + tm0 = 0; /* Keep gcc -Wall happy. */ + localzone = pt->zone==TM_LOCAL_ZONE; + + tm = pt->tm; + + if (TM_DEFINED(pt->ymodulus) || !TM_DEFINED(tm.tm_year)) { + /* Get tm corresponding to current time. */ + tm0 = time2tm(default_time, localzone); + if (!localzone) + adjzone(tm0, pt->zone); + } + + if (TM_DEFINED(pt->ymodulus)) + tm.tm_year += + (tm0->tm_year + TM_YEAR_ORIGIN)/pt->ymodulus * pt->ymodulus; + else if (!TM_DEFINED(tm.tm_year)) { + /* Set default year, month, day from current time. */ + tm.tm_year = tm0->tm_year + TM_YEAR_ORIGIN; + if (!TM_DEFINED(tm.tm_mon)) { + tm.tm_mon = tm0->tm_mon; + if (!TM_DEFINED(tm.tm_mday)) + tm.tm_mday = tm0->tm_mday; + } + } + + /* Convert from partime year (Gregorian) to Posix year. */ + tm.tm_year -= TM_YEAR_ORIGIN; + + /* Set remaining default fields to be their minimum values. */ + if (!TM_DEFINED(tm.tm_mon)) tm.tm_mon = 0; + if (!TM_DEFINED(tm.tm_mday)) tm.tm_mday = 1; + if (!TM_DEFINED(tm.tm_hour)) tm.tm_hour = 0; + if (!TM_DEFINED(tm.tm_min)) tm.tm_min = 0; + if (!TM_DEFINED(tm.tm_sec)) tm.tm_sec = 0; + + if (!localzone) + adjzone(&tm, -pt->zone); + wday = tm.tm_wday; + + /* Convert and fill in the rest of the tm. */ + r = tm2time(&tm, localzone); + + /* Check weekday. */ + if (r != -1 && TM_DEFINED(wday) && wday != tm.tm_wday) + return -1; + + return r; +} + +/* Parse a free-format date in SOURCE, yielding a Unix format time. */ + time_t +str2time(source, default_time, default_zone) + char const *source; + time_t default_time; + long default_zone; +{ + struct partime pt; + + if (*partime(source, &pt)) + return -1; + if (pt.zone == TM_UNDEFINED_ZONE) + pt.zone = default_zone; + return maketime(&pt, default_time); +} + +#if TEST +#include + int +main(argc, argv) int argc; char **argv; +{ + time_t default_time = time((time_t *)0); + long default_zone = argv[1] ? atol(argv[1]) : 0; + char buf[1000]; + while (fgets(buf, 1000, stdin)) { + time_t t = str2time(buf, default_time, default_zone); + printf("%s", asctime(gmtime(&t))); + } + return 0; +} +#endif diff --git a/gnu/usr.bin/rcs/lib/maketime.h b/gnu/usr.bin/rcs/lib/maketime.h new file mode 100644 index 00000000000..fbe12562051 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/maketime.h @@ -0,0 +1,39 @@ +/* Yield time_t from struct partime yielded by partime. */ + +/* Copyright 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#if defined(__STDC__) || has_prototypes +# define __MAKETIME_P(x) x +#else +# define __MAKETIME_P(x) () +#endif + +struct tm *time2tm __MAKETIME_P((time_t,int)); +time_t difftm __MAKETIME_P((struct tm const *, struct tm const *)); +time_t str2time __MAKETIME_P((char const *, time_t, long)); +time_t tm2time __MAKETIME_P((struct tm *, int)); +void adjzone __MAKETIME_P((struct tm *, long)); diff --git a/gnu/usr.bin/rcs/lib/merger.c b/gnu/usr.bin/rcs/lib/merger.c new file mode 100644 index 00000000000..8f1d610d5f5 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/merger.c @@ -0,0 +1,148 @@ +/* three-way file merge internals */ + +/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +libId(mergerId, "$FreeBSD$") + + static char const *normalize_arg P((char const*,char**)); + static char const * +normalize_arg(s, b) + char const *s; + char **b; +/* + * If S looks like an option, prepend ./ to it. Yield the result. + * Set *B to the address of any storage that was allocated. + */ +{ + char *t; + if (*s == '-') { + *b = t = testalloc(strlen(s) + 3); + VOID sprintf(t, ".%c%s", SLASH, s); + return t; + } else { + *b = 0; + return s; + } +} + + int +merge(tostdout, edarg, label, argv) + int tostdout; + char const *edarg; + char const *const label[3]; + char const *const argv[3]; +/* + * Do `merge [-p] EDARG -L l0 -L l1 -L l2 a0 a1 a2', + * where TOSTDOUT specifies whether -p is present, + * EDARG gives the editing type (e.g. "-A", or null for the default), + * LABEL gives l0, l1 and l2, and ARGV gives a0, a1 and a2. + * Yield DIFF_SUCCESS or DIFF_FAILURE. + */ +{ + register int i; + FILE *f; + RILE *rt; + char const *a[3], *t; + char *b[3]; + int s; +#if !DIFF3_BIN + char const *d[2]; +#endif + + for (i=3; 0<=--i; ) + a[i] = normalize_arg(argv[i], &b[i]); + + if (!edarg) + edarg = "-E"; + +#if DIFF3_BIN + t = 0; + if (!tostdout) + t = maketemp(0); + s = run( + -1, t, + DIFF3, edarg, "-am", + "-L", label[0], + "-L", label[1], + "-L", label[2], + a[0], a[1], a[2], (char*)0 + ); + switch (s) { + case DIFF_SUCCESS: + break; + case DIFF_FAILURE: + warn("conflicts during merge"); + break; + default: + exiterr(); + } + if (t) { + if (!(f = fopenSafer(argv[0], "w"))) + efaterror(argv[0]); + if (!(rt = Iopen(t, "r", (struct stat*)0))) + efaterror(t); + fastcopy(rt, f); + Ifclose(rt); + Ofclose(f); + } +#else + for (i=0; i<2; i++) + switch (run( + -1, d[i]=maketemp(i), + DIFF, a[i], a[2], (char*)0 + )) { + case DIFF_FAILURE: case DIFF_SUCCESS: break; + default: faterror("diff failed"); + } + t = maketemp(2); + s = run( + -1, t, + DIFF3, edarg, d[0], d[1], a[0], a[1], a[2], + label[0], label[2], (char*)0 + ); + if (s != DIFF_SUCCESS) { + s = DIFF_FAILURE; + warn("overlaps or other problems during merge"); + } + if (!(f = fopenSafer(t, "a+"))) + efaterror(t); + aputs(tostdout ? "1,$p\n" : "w\n", f); + Orewind(f); + aflush(f); + if (run(fileno(f), (char*)0, ED, "-", a[0], (char*)0)) + exiterr(); + Ofclose(f); +#endif + + tempunlink(); + for (i=3; 0<=--i; ) + if (b[i]) + tfree(b[i]); + return s; +} diff --git a/gnu/usr.bin/rcs/lib/partime.c b/gnu/usr.bin/rcs/lib/partime.c new file mode 100644 index 00000000000..05b01085495 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/partime.c @@ -0,0 +1,701 @@ +/* Parse a string, yielding a struct partime that describes it. */ + +/* Copyright 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#if has_conf_h +# include "conf.h" +#else +# ifdef __STDC__ +# define P(x) x +# else +# define const +# define P(x) () +# endif +# include +# include +#endif + +#include +#undef isdigit +#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than stock */ + +#include "partime.h" + +char const partimeId[] + = "$FreeBSD$"; + + +/* Lookup tables for names of months, weekdays, time zones. */ + +#define NAME_LENGTH_MAXIMUM 4 + +struct name_val { + char name[NAME_LENGTH_MAXIMUM]; + int val; +}; + + +static char const *parse_decimal P((char const*,int,int,int,int,int*,int*)); +static char const *parse_fixed P((char const*,int,int*)); +static char const *parse_pattern_letter P((char const*,int,struct partime*)); +static char const *parse_prefix P((char const*,struct partime*,int*)); +static char const *parse_ranged P((char const*,int,int,int,int*)); +static int lookup P((char const*,struct name_val const[])); +static int merge_partime P((struct partime*, struct partime const*)); +static void undefine P((struct partime*)); + + +static struct name_val const month_names[] = { + {"jan",0}, {"feb",1}, {"mar",2}, {"apr",3}, {"may",4}, {"jun",5}, + {"jul",6}, {"aug",7}, {"sep",8}, {"oct",9}, {"nov",10}, {"dec",11}, + {"", TM_UNDEFINED} +}; + +static struct name_val const weekday_names[] = { + {"sun",0}, {"mon",1}, {"tue",2}, {"wed",3}, {"thu",4}, {"fri",5}, {"sat",6}, + {"", TM_UNDEFINED} +}; + +#define hr60nonnegative(t) ((t)/100 * 60 + (t)%100) +#define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t)) +#define zs(t,s) {s, hr60(t)} +#define zd(t,s,d) zs(t, s), zs((t)+100, d) + +static struct name_val const zone_names[] = { + zs(-1000, "hst"), /* Hawaii */ + zd(-1000,"hast","hadt"),/* Hawaii-Aleutian */ + zd(- 900,"akst","akdt"),/* Alaska */ + zd(- 800, "pst", "pdt"),/* Pacific */ + zd(- 700, "mst", "mdt"),/* Mountain */ + zd(- 600, "cst", "cdt"),/* Central */ + zd(- 500, "est", "edt"),/* Eastern */ + zd(- 400, "ast", "adt"),/* Atlantic */ + zd(- 330, "nst", "ndt"),/* Newfoundland */ + zs( 000, "utc"), /* Coordinated Universal */ + zs( 000, "cut"), /* " */ + zs( 000, "ut"), /* Universal */ + zs( 000, "z"), /* Zulu (required by ISO 8601) */ + zd( 000, "gmt", "bst"),/* Greenwich Mean, British Summer */ + zs( 000, "wet"), /* Western Europe */ + zs( 100, "met"), /* Middle Europe */ + zs( 100, "cet"), /* Central Europe */ + zs( 200, "eet"), /* Eastern Europe */ + zs( 530, "ist"), /* India */ + zd( 900, "jst", "jdt"),/* Japan */ + zd( 900, "kst", "kdt"),/* Korea */ + zd( 1200,"nzst","nzdt"),/* New Zealand */ + { "lt", 1 }, +#if 0 + /* The following names are duplicates or are not well attested. */ + zs(-1100, "sst"), /* Samoa */ + zs(-1000, "tht"), /* Tahiti */ + zs(- 930, "mqt"), /* Marquesas */ + zs(- 900, "gbt"), /* Gambier */ + zd(- 900, "yst", "ydt"),/* Yukon - name is no longer used */ + zs(- 830, "pit"), /* Pitcairn */ + zd(- 500, "cst", "cdt"),/* Cuba */ + zd(- 500, "ast", "adt"),/* Acre */ + zd(- 400, "wst", "wdt"),/* Western Brazil */ + zd(- 400, "ast", "adt"),/* Andes */ + zd(- 400, "cst", "cdt"),/* Chile */ + zs(- 300, "wgt"), /* Western Greenland */ + zd(- 300, "est", "edt"),/* Eastern South America */ + zs(- 300, "mgt"), /* Middle Greenland */ + zd(- 200, "fst", "fdt"),/* Fernando de Noronha */ + zs(- 100, "egt"), /* Eastern Greenland */ + zs(- 100, "aat"), /* Atlantic Africa */ + zs(- 100, "act"), /* Azores and Canaries */ + zs( 000, "wat"), /* West Africa */ + zs( 100, "cat"), /* Central Africa */ + zd( 100, "mez","mesz"),/* Mittel-Europaeische Zeit */ + zs( 200, "sat"), /* South Africa */ + zd( 200, "ist", "idt"),/* Israel */ + zs( 300, "eat"), /* East Africa */ + zd( 300, "ast", "adt"),/* Arabia */ + zd( 300, "msk", "msd"),/* Moscow */ + zd( 330, "ist", "idt"),/* Iran */ + zs( 400, "gst"), /* Gulf */ + zs( 400, "smt"), /* Seychelles & Mascarene */ + zd( 400, "esk", "esd"),/* Yekaterinburg */ + zd( 400, "bsk", "bsd"),/* Baku */ + zs( 430, "aft"), /* Afghanistan */ + zd( 500, "osk", "osd"),/* Omsk */ + zs( 500, "pkt"), /* Pakistan */ + zd( 500, "tsk", "tsd"),/* Tashkent */ + zs( 545, "npt"), /* Nepal */ + zs( 600, "bgt"), /* Bangladesh */ + zd( 600, "nsk", "nsd"),/* Novosibirsk */ + zs( 630, "bmt"), /* Burma */ + zs( 630, "cct"), /* Cocos */ + zs( 700, "ict"), /* Indochina */ + zs( 700, "jvt"), /* Java */ + zd( 700, "isk", "isd"),/* Irkutsk */ + zs( 800, "hkt"), /* Hong Kong */ + zs( 800, "pst"), /* Philippines */ + zs( 800, "sgt"), /* Singapore */ + zd( 800, "cst", "cdt"),/* China */ + zd( 800, "ust", "udt"),/* Ulan Bator */ + zd( 800, "wst", "wst"),/* Western Australia */ + zd( 800, "ysk", "ysd"),/* Yakutsk */ + zs( 900, "blt"), /* Belau */ + zs( 900, "mlt"), /* Moluccas */ + zd( 900, "vsk", "vsd"),/* Vladivostok */ + zd( 930, "cst", "cst"),/* Central Australia */ + zs( 1000, "gst"), /* Guam */ + zd( 1000, "gsk", "gsd"),/* Magadan */ + zd( 1000, "est", "est"),/* Eastern Australia */ + zd( 1100,"lhst","lhst"),/* Lord Howe */ + zd( 1100, "psk", "psd"),/* Petropavlovsk-Kamchatski */ + zs( 1100,"ncst"), /* New Caledonia */ + zs( 1130,"nrft"), /* Norfolk */ + zd( 1200, "ask", "asd"),/* Anadyr */ + zs( 1245,"nz-chat"), /* Chatham */ + zs( 1300, "tgt"), /* Tongatapu */ +#endif + {"", -1} +}; + + static int +lookup (s, table) + char const *s; + struct name_val const table[]; +/* Look for a prefix of S in TABLE, returning val for first matching entry. */ +{ + int j; + char buf[NAME_LENGTH_MAXIMUM]; + + for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) { + unsigned char c = *s++; + buf[j] = isupper (c) ? tolower (c) : c; + if (!isalpha (c)) + break; + } + for (; table[0].name[0]; table++) + for (j = 0; buf[j] == table[0].name[j]; ) + if (++j == NAME_LENGTH_MAXIMUM || !table[0].name[j]) + goto done; + done: + return table[0].val; +} + + + static void +undefine (t) struct partime *t; +/* Set *T to ``undefined'' values. */ +{ + t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon + = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday + = t->ymodulus = t->yweek + = TM_UNDEFINED; + t->zone = TM_UNDEFINED_ZONE; +} + +/* +* Array of patterns to look for in a date string. +* Order is important: we look for the first matching pattern +* whose values do not contradict values that we already know about. +* See `parse_pattern_letter' below for the meaning of the pattern codes. +*/ +static char const * const patterns[] = { + /* + * These traditional patterns must come first, + * to prevent an ISO 8601 format from misinterpreting their prefixes. + */ + "E_n_y", "x", /* RFC 822 */ + "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */ + "y/N/D$", /* traditional RCS */ + + /* ISO 8601:1988 formats, generalized a bit. */ + "y-N-D$", "4ND$", "Y-N$", + "RND$", "-R=N$", "-R$", "--N=D$", "N=DT", + "--N$", "---D$", "DT", + "Y-d$", "4d$", "R=d$", "-d$", "dT", + "y-W-X", "yWX", "y=W", + "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W", + "-w-X", "w-XT", "---X$", "XT", "4$", + "T", + "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$", + "Y", "Z", + + 0 +}; + + static char const * +parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi; +/* +* Parse an initial prefix of STR, setting *T accordingly. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +* Start with pattern *PI; if success, set *PI to the next pattern to try. +* Set *PI to -1 if we know there are no more patterns to try; +* if *PI is initially negative, give up immediately. +*/ +{ + int i = *pi; + char const *pat; + unsigned char c; + + if (i < 0) + return 0; + + /* Remove initial noise. */ + while (!isalnum (c = *str) && c != '-' && c != '+') { + if (!c) { + undefine (t); + *pi = -1; + return str; + } + str++; + } + + /* Try a pattern until one succeeds. */ + while ((pat = patterns[i++]) != 0) { + char const *s = str; + undefine (t); + do { + if (!(c = *pat++)) { + *pi = i; + return s; + } + } while ((s = parse_pattern_letter (s, c, t)) != 0); + } + + return 0; +} + + static char const * +parse_fixed (s, digits, res) char const *s; int digits, *res; +/* +* Parse an initial prefix of S of length DIGITS; it must be a number. +* Store the parsed number into *RES. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + int n = 0; + char const *lim = s + digits; + while (s < lim) { + unsigned d = *s++ - '0'; + if (9 < d) + return 0; + n = 10*n + d; + } + *res = n; + return s; +} + + static char const * +parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res; +/* +* Parse an initial prefix of S of length DIGITS; +* it must be a number in the range LO through HI. +* Store the parsed number into *RES. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + s = parse_fixed (s, digits, res); + return s && lo<=*res && *res<=hi ? s : 0; +} + + static char const * +parse_decimal (s, digits, lo, hi, resolution, res, fres) + char const *s; + int digits, lo, hi, resolution, *res, *fres; +/* +* Parse an initial prefix of S of length DIGITS; +* it must be a number in the range LO through HI +* and it may be followed by a fraction that is to be computed using RESOLUTION. +* Store the parsed number into *RES; store the fraction times RESOLUTION, +* rounded to the nearest integer, into *FRES. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + s = parse_fixed (s, digits, res); + if (s && lo<=*res && *res<=hi) { + int f = 0; + if ((s[0]==',' || s[0]=='.') && isdigit ((unsigned char) s[1])) { + char const *s1 = ++s; + int num10 = 0, denom10 = 10, product; + while (isdigit ((unsigned char) *++s)) + denom10 *= 10; + s = parse_fixed (s1, s - s1, &num10); + product = num10*resolution; + f = (product + (denom10>>1)) / denom10; + f -= f & (product%denom10 == denom10>>1); /* round to even */ + if (f < 0 || product/resolution != num10) + return 0; /* overflow */ + } + *fres = f; + return s; + } + return 0; +} + + char * +parzone (s, zone) char const *s; long *zone; +/* +* Parse an initial prefix of S; it must denote a time zone. +* Set *ZONE to the number of seconds east of GMT, +* or to TM_LOCAL_ZONE if it is the local time zone. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + char sign; + int hh, mm, ss; + int minutesEastOfUTC; + long offset, z; + + /* + * The formats are LT, n, n DST, nDST, no, o + * where n is a time zone name + * and o is a time zone offset of the form [-+]hh[:mm[:ss]]. + */ + switch (*s) { + case '-': case '+': + z = 0; + break; + + default: + minutesEastOfUTC = lookup (s, zone_names); + if (minutesEastOfUTC == -1) + return 0; + + /* Don't bother to check rest of spelling. */ + while (isalpha ((unsigned char) *s)) + s++; + + /* Don't modify LT. */ + if (minutesEastOfUTC == 1) { + *zone = TM_LOCAL_ZONE; + return (char *) s; + } + + z = minutesEastOfUTC * 60L; + + /* Look for trailing " DST". */ + if ( + (s[-1]=='T' || s[-1]=='t') && + (s[-2]=='S' || s[-2]=='s') && + (s[-3]=='D' || s[-3]=='t') + ) + goto trailing_dst; + while (isspace ((unsigned char) *s)) + s++; + if ( + (s[0]=='D' || s[0]=='d') && + (s[1]=='S' || s[1]=='s') && + (s[2]=='T' || s[2]=='t') + ) { + s += 3; + trailing_dst: + *zone = z + 60*60; + return (char *) s; + } + + switch (*s) { + case '-': case '+': break; + default: return (char *) s; + } + } + sign = *s++; + + if (!(s = parse_ranged (s, 2, 0, 23, &hh))) + return 0; + mm = ss = 0; + if (*s == ':') + s++; + if (isdigit ((unsigned char) *s)) { + if (!(s = parse_ranged (s, 2, 0, 59, &mm))) + return 0; + if (*s==':' && s[-3]==':' && isdigit ((unsigned char) s[1])) { + if (!(s = parse_ranged (s + 1, 2, 0, 59, &ss))) + return 0; + } + } + if (isdigit ((unsigned char) *s)) + return 0; + offset = (hh*60 + mm)*60L + ss; + *zone = z + (sign=='-' ? -offset : offset); + /* + * ?? Are fractions allowed here? + * If so, they're not implemented. + */ + return (char *) s; +} + + static char const * +parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t; +/* +* Parse an initial prefix of S, matching the pattern whose code is C. +* Set *T accordingly. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + switch (c) { + case '$': /* The next character must be a non-digit. */ + if (isdigit ((unsigned char) *s)) + return 0; + break; + + case '-': case '/': case ':': + /* These characters stand for themselves. */ + if (*s++ != c) + return 0; + break; + + case '4': /* 4-digit year */ + s = parse_fixed (s, 4, &t->tm.tm_year); + break; + + case '=': /* optional '-' */ + s += *s == '-'; + break; + + case 'A': /* AM or PM */ + /* + * This matches the regular expression [AaPp][Mm]?. + * It must not be followed by a letter or digit; + * otherwise it would match prefixes of strings like "PST". + */ + switch (*s++) { + case 'A': case 'a': + if (t->tm.tm_hour == 12) + t->tm.tm_hour = 0; + break; + + case 'P': case 'p': + if (t->tm.tm_hour != 12) + t->tm.tm_hour += 12; + break; + + default: return 0; + } + switch (*s) { + case 'M': case 'm': s++; break; + } + if (isalnum (*s)) + return 0; + break; + + case 'D': /* day of month [01-31] */ + s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday); + break; + + case 'd': /* day of year [001-366] */ + s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday); + t->tm.tm_yday--; + break; + + case 'E': /* extended day of month [1-9, 01-31] */ + s = parse_ranged (s, ( + isdigit ((unsigned char) s[0]) && + isdigit ((unsigned char) s[1]) + ) + 1, 1, 31, &t->tm.tm_mday); + break; + + case 'h': /* hour [00-23 followed by optional fraction] */ + { + int frac; + s = parse_decimal (s, 2, 0, 23, 60*60, &t->tm.tm_hour, &frac); + t->tm.tm_min = frac / 60; + t->tm.tm_sec = frac % 60; + } + break; + + case 'm': /* minute [00-59 followed by optional fraction] */ + s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec); + break; + + case 'n': /* month name [e.g. "Jan"] */ + if (!TM_DEFINED (t->tm.tm_mon = lookup (s, month_names))) + return 0; + /* Don't bother to check rest of spelling. */ + while (isalpha ((unsigned char) *s)) + s++; + break; + + case 'N': /* month [01-12] */ + s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon); + t->tm.tm_mon--; + break; + + case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */ + s = parse_fixed (s, 1, &t->tm.tm_year); + t->ymodulus = 10; + break; + + case_R: + case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */ + s = parse_fixed (s, 2, &t->tm.tm_year); + t->ymodulus = 100; + break; + + case 's': /* second [00-60 followed by optional fraction] */ + { + int frac; + s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac); + t->tm.tm_sec += frac; + } + break; + + case 'T': /* 'T' or 't' */ + switch (*s++) { + case 'T': case 't': break; + default: return 0; + } + break; + + case 't': /* traditional hour [1-9 or 01-12] */ + s = parse_ranged (s, ( + isdigit ((unsigned char) s[0]) && isdigit ((unsigned char) s[1]) + ) + 1, 1, 12, &t->tm.tm_hour); + break; + + case 'w': /* 'W' or 'w' only (stands for current week) */ + switch (*s++) { + case 'W': case 'w': break; + default: return 0; + } + break; + + case 'W': /* 'W' or 'w', followed by a week of year [00-53] */ + switch (*s++) { + case 'W': case 'w': break; + default: return 0; + } + s = parse_ranged (s, 2, 0, 53, &t->yweek); + break; + + case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */ + s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday); + t->tm.tm_wday--; + break; + + case 'x': /* weekday name [e.g. "Sun"] */ + if (!TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names))) + return 0; + /* Don't bother to check rest of spelling. */ + while (isalpha ((unsigned char) *s)) + s++; + break; + + case 'y': /* either R or Y */ + if ( + isdigit ((unsigned char) s[0]) && + isdigit ((unsigned char) s[1]) && + !isdigit ((unsigned char) s[2]) + ) + goto case_R; + /* fall into */ + case 'Y': /* year in full [4 or more digits] */ + { + int len = 0; + while (isdigit ((unsigned char) s[len])) + len++; + if (len < 4) + return 0; + s = parse_fixed (s, len, &t->tm.tm_year); + } + break; + + case 'Z': /* time zone */ + s = parzone (s, &t->zone); + break; + + case '_': /* possibly empty sequence of non-alphanumerics */ + while (!isalnum (*s) && *s) + s++; + break; + + default: /* bad pattern */ + return 0; + } + return s; +} + + static int +merge_partime (t, u) struct partime *t; struct partime const *u; +/* +* If there is no conflict, merge into *T the additional information in *U +* and return 0. Otherwise do nothing and return -1. +*/ +{ +# define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b)) + if ( + conflict (t->tm.tm_sec, u->tm.tm_sec) || + conflict (t->tm.tm_min, u->tm.tm_min) || + conflict (t->tm.tm_hour, u->tm.tm_hour) || + conflict (t->tm.tm_mday, u->tm.tm_mday) || + conflict (t->tm.tm_mon, u->tm.tm_mon) || + conflict (t->tm.tm_year, u->tm.tm_year) || + conflict (t->tm.tm_wday, u->tm.tm_yday) || + conflict (t->ymodulus, u->ymodulus) || + conflict (t->yweek, u->yweek) || + ( + t->zone != u->zone && + t->zone != TM_UNDEFINED_ZONE && + u->zone != TM_UNDEFINED_ZONE + ) + ) + return -1; +# undef conflict +# define merge_(a,b) if (TM_DEFINED (b)) (a) = (b); + merge_ (t->tm.tm_sec, u->tm.tm_sec) + merge_ (t->tm.tm_min, u->tm.tm_min) + merge_ (t->tm.tm_hour, u->tm.tm_hour) + merge_ (t->tm.tm_mday, u->tm.tm_mday) + merge_ (t->tm.tm_mon, u->tm.tm_mon) + merge_ (t->tm.tm_year, u->tm.tm_year) + merge_ (t->tm.tm_wday, u->tm.tm_yday) + merge_ (t->ymodulus, u->ymodulus) + merge_ (t->yweek, u->yweek) +# undef merge_ + if (u->zone != TM_UNDEFINED_ZONE) t->zone = u->zone; + return 0; +} + + char * +partime (s, t) char const *s; struct partime *t; +/* +* Parse a date/time prefix of S, putting the parsed result into *T. +* Return the first character after the prefix. +* The prefix may contain no useful information; +* in that case, *T will contain only undefined values. +*/ +{ + struct partime p; + + undefine (t); + while (*s) { + int i = 0; + char const *s1; + do { + if (!(s1 = parse_prefix (s, &p, &i))) + return (char *) s; + } while (merge_partime (t, &p) != 0); + s = s1; + } + return (char *) s; +} diff --git a/gnu/usr.bin/rcs/lib/partime.h b/gnu/usr.bin/rcs/lib/partime.h new file mode 100644 index 00000000000..5d3983fbb04 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/partime.h @@ -0,0 +1,71 @@ +/* Parse a string, yielding a struct partime that describes it. */ + +/* Copyright 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#define TM_UNDEFINED (-1) +#define TM_DEFINED(x) (0 <= (x)) + +#define TM_UNDEFINED_ZONE ((long) -24 * 60 * 60) +#define TM_LOCAL_ZONE (TM_UNDEFINED_ZONE - 1) + +struct partime { + /* + * This structure describes the parsed time. + * Only the following tm_* values in it are used: + * sec, min, hour, mday, mon, year, wday, yday. + * If TM_UNDEFINED(value), the parser never found the value. + * The tm_year field is the actual year, not the year - 1900; + * but see ymodulus below. + */ + struct tm tm; + + /* + * If !TM_UNDEFINED(ymodulus), + * then tm.tm_year is actually modulo ymodulus. + */ + int ymodulus; + + /* + * Week of year, ISO 8601 style. + * If TM_UNDEFINED(yweek), the parser never found yweek. + * Weeks start on Mondays. + * Week 1 includes Jan 4. + */ + int yweek; + + /* Seconds east of UTC; or TM_LOCAL_ZONE or TM_UNDEFINED_ZONE. */ + long zone; +}; + +#if defined(__STDC__) || has_prototypes +# define __PARTIME_P(x) x +#else +# define __PARTIME_P(x) () +#endif + +char *partime __PARTIME_P((char const *, struct partime *)); +char *parzone __PARTIME_P((char const *, long *)); diff --git a/gnu/usr.bin/rcs/lib/rcsbase.h b/gnu/usr.bin/rcs/lib/rcsbase.h new file mode 100644 index 00000000000..9f2f68cc8b6 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsbase.h @@ -0,0 +1,762 @@ +/* RCS common definitions and data structures */ + +#define RCSBASE "$FreeBSD$" + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.20 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.19 1995/06/01 16:23:43 eggert + * (SIZEABLE_PATH): Don't depend on PATH_MAX: it's not worth configuring. + * (Ioffset_type,BINARY_EXPAND,MIN_UNEXPAND,MIN_UNCHANGED_EXPAND): New macros. + * (maps_memory): New macro; replaces many instances of `has_mmap'. + * (cacheptr): Renamed from cachetell. + * (struct RILE): New alternate name for RILE; the type is now recursive. + * (deallocate): New member for RILE, used for generic buffer deallocation. + * (cacheunget_): No longer take a failure arg; just call Ierror on failure. + * (struct rcslock): Renamed from struct lock, to avoid collisions with + * system headers on some hosts. All users changed. + * (basefilename): Renamed from basename, likewise. + * (dirtpname): Remove; no longer external. + * (dirlen, dateform): Remove; no longer used. + * (cmpdate, fopenSafer, fdSafer, readAccessFilenameBuffer): New functions. + * (zonelenmax): Increase to 9 for full ISO 8601 format. + * (catchmmapints): Depend on has_NFS. + * + * Revision 5.18 1994/03/17 14:05:48 eggert + * Add primitives for reading backwards from a RILE; + * this is needed to go back and find the $Log prefix. + * Specify subprocess input via file descriptor, not file name. Remove lint. + * + * Revision 5.17 1993/11/09 17:40:15 eggert + * Move RCS-specific time handling into rcstime.c. + * printf_string now takes two arguments, alas. + * + * Revision 5.16 1993/11/03 17:42:27 eggert + * Don't arbitrarily limit the number of joins. Remove `nil'. + * Add Name keyword. Don't discard ignored phrases. + * Add support for merge -A vs -E, and allow up to three labels. + * Improve quality of diagnostics and prototypes. + * + * Revision 5.15 1992/07/28 16:12:44 eggert + * Statement macro names now end in _. + * + * Revision 5.14 1992/02/17 23:02:22 eggert + * Add -T support. Work around NFS mmap SIGBUS problem. + * + * Revision 5.13 1992/01/24 18:44:19 eggert + * Add support for bad_creat0. lint -> RCS_lint + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * while (E) ; -> while (E) continue; + * + * Revision 5.11 1991/10/07 17:32:46 eggert + * Support piece tables even if !has_mmap. + * + * Revision 5.10 1991/09/24 00:28:39 eggert + * Remove unexported functions. + * + * Revision 5.9 1991/08/19 03:13:55 eggert + * Add piece tables and other tuneups, and NFS workarounds. + * + * Revision 5.8 1991/04/21 11:58:20 eggert + * Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.7 1991/02/28 19:18:50 eggert + * Try setuid() if seteuid() doesn't work. + * + * Revision 5.6 1991/02/26 17:48:37 eggert + * Support new link behavior. Move ANSI C / Posix declarations into conf.sh. + * + * Revision 5.5 1990/12/04 05:18:43 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:35 eggert + * Don't assume that builtins are functions; they may be macros. + * Permit arbitrary data in logs. + * + * Revision 5.3 1990/09/26 23:36:58 eggert + * Port wait() to non-Posix ANSI C hosts. + * + * Revision 5.2 1990/09/04 08:02:20 eggert + * Don't redefine NAME_MAX, PATH_MAX. + * Improve incomplete line handling. Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:13:53 eggert + * Add -kkvl. Fix type typos exposed by porting. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:44 eggert + * Adjust ANSI C / Posix support. Add -k, -V, setuid. Don't call access(). + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. Add support for ISO 8859. + * Remove snoop and v2 support. + * + * Revision 4.9 89/05/01 15:17:14 narten + * botched previous USG fix + * + * Revision 4.8 89/05/01 14:53:05 narten + * changed #include -> string.h for USG systems. + * + * Revision 4.7 88/11/08 15:58:45 narten + * removed defs for functions loaded from libraries + * + * Revision 4.6 88/08/09 19:12:36 eggert + * Shrink stdio code size; remove lint; permit -Dhshsize=nn. + * + * Revision 4.5 87/12/18 17:06:41 narten + * made removed BSD ifdef, now uses V4_2BSD + * + * Revision 4.4 87/10/18 10:29:49 narten + * Updating version numbers + * Changes relative to 1.1 are actually relative to 4.2 + * + * Revision 1.3 87/09/24 14:02:25 narten + * changes for lint + * + * Revision 1.2 87/03/27 14:22:02 jenkins + * Port to suns + * + * Revision 4.2 83/12/20 16:04:20 wft + * merged 3.6.1.1 and 4.1 (SMALLOG, logsize). + * moved setting of STRICT_LOCKING to Makefile. + * changed DOLLAR to UNKN (conflict with KDELIM). + * + * Revision 4.1 83/05/04 09:12:41 wft + * Added markers Id and RCSfile. + * Added Dbranch for default branches. + * + * Revision 3.6.1.1 83/12/02 21:56:22 wft + * Increased logsize, added macro SMALLOG. + * + * Revision 3.6 83/01/15 16:43:28 wft + * 4.2 prerelease + * + * Revision 3.6 83/01/15 16:43:28 wft + * Replaced dbm.h with BYTESIZ, fixed definition of rindex(). + * Added variants of NCPFN and NCPPN for bsd 4.2, selected by defining V4_2BSD. + * Added macro DELNUMFORM to have uniform format for printing delta text nodes. + * Added macro DELETE to mark deleted deltas. + * + * Revision 3.5 82/12/10 12:16:56 wft + * Added two forms of DATEFORM, one using %02d, the other %.2d. + * + * Revision 3.4 82/12/04 20:01:25 wft + * added LOCKER, Locker, and USG (redefinition of rindex). + * + * Revision 3.3 82/12/03 12:22:04 wft + * Added dbm.h, stdio.h, RCSBASE, RCSSEP, RCSSUF, WORKMODE, TMPFILE3, + * PRINTDATE, PRINTTIME, map, and ctab; removed Suffix. Redefined keyvallength + * using NCPPN. Changed putc() to abort on write error. + * + * Revision 3.2 82/10/18 15:03:52 wft + * added macro STRICT_LOCKING, removed RCSUMASK. + * renamed JOINFILE[1,2] to JOINFIL[1,2]. + * + * Revision 3.1 82/10/11 19:41:17 wft + * removed NBPW, NBPC, NCPW. + * added typdef int void to aid compiling + */ + + +#include "conf.h" + + +#define EXIT_TROUBLE DIFF_TROUBLE + +#ifdef _POSIX_PATH_MAX +# define SIZEABLE_PATH _POSIX_PATH_MAX +#else +# define SIZEABLE_PATH 255 /* size of a large path; not a hard limit */ +#endif + +/* for traditional C hosts with unusual size arguments */ +#define Fread(p,s,n,f) fread(p, (freadarg_type)(s), (freadarg_type)(n), f) +#define Fwrite(p,s,n,f) fwrite(p, (freadarg_type)(s), (freadarg_type)(n), f) + + +/* + * Parameters + */ + +/* backwards compatibility with old versions of RCS */ +#define VERSION_min 3 /* old output RCS format supported */ +#define VERSION_max 5 /* newest output RCS format supported */ +#ifndef VERSION_DEFAULT /* default RCS output format */ +# define VERSION_DEFAULT VERSION_max +#endif +#define VERSION(n) ((n) - VERSION_DEFAULT) /* internally, 0 is the default */ + +#ifndef STRICT_LOCKING +#define STRICT_LOCKING 1 +#endif + /* 0 sets the default locking to non-strict; */ + /* used in experimental environments. */ + /* 1 sets the default locking to strict; */ + /* used in production environments. */ + +#define yearlength 16 /* (good through AD 9,999,999,999,999,999) */ +#define datesize (yearlength+16) /* size of output of time2date */ +#define RCSTMPPREFIX '_' /* prefix for temp files in working dir */ +#define KDELIM '$' /* delimiter for keywords */ +#define VDELIM ':' /* separates keywords from values */ +#define DEFAULTSTATE "Exp" /* default state of revisions */ + + + +#define true 1 +#define false 0 + + +/* + * RILE - readonly file + * declarecache; - declares local cache for RILE variable(s) + * setupcache - sets up the local RILE cache, but does not initialize it + * cache, uncache - caches and uncaches the local RILE; + * (uncache,cache) is needed around functions that advance the RILE pointer + * Igeteof_(f,c,s) - get a char c from f, executing statement s at EOF + * cachegeteof_(c,s) - Igeteof_ applied to the local RILE + * Iget_(f,c) - like Igeteof_, except EOF is an error + * cacheget_(c) - Iget_ applied to the local RILE + * cacheunget_(f,c,s) - read c backwards from cached f, executing s at BOF + * Ifileno, Ioffset_type, Irewind, Itell - analogs to stdio routines + * + * By conventions, macros whose names end in _ are statements, not expressions. + * Following such macros with `; else' results in a syntax error. + */ + +#define maps_memory (has_map_fd || has_mmap) + +#if large_memory + typedef unsigned char const *Iptr_type; + typedef struct RILE { + Iptr_type ptr, lim; + unsigned char *base; /* not Iptr_type for lint's sake */ + unsigned char *readlim; + int fd; +# if maps_memory + void (*deallocate) P((struct RILE *)); +# else + FILE *stream; +# endif + } RILE; +# if maps_memory +# define declarecache register Iptr_type ptr, lim +# define setupcache(f) (lim = (f)->lim) +# define Igeteof_(f,c,s) if ((f)->ptr==(f)->lim) s else (c)= *(f)->ptr++; +# define cachegeteof_(c,s) if (ptr==lim) s else (c)= *ptr++; +# else + int Igetmore P((RILE*)); +# define declarecache register Iptr_type ptr; register RILE *rRILE +# define setupcache(f) (rRILE = (f)) +# define Igeteof_(f,c,s) if ((f)->ptr==(f)->readlim && !Igetmore(f)) s else (c)= *(f)->ptr++; +# define cachegeteof_(c,s) if (ptr==rRILE->readlim && !Igetmore(rRILE)) s else (c)= *ptr++; +# endif +# define uncache(f) ((f)->ptr = ptr) +# define cache(f) (ptr = (f)->ptr) +# define Iget_(f,c) Igeteof_(f,c,Ieof();) +# define cacheget_(c) cachegeteof_(c,Ieof();) +# define cacheunget_(f,c) (c)=(--ptr)[-1]; +# define Ioffset_type size_t +# define Itell(f) ((f)->ptr - (f)->base) +# define Irewind(f) ((f)->ptr = (f)->base) +# define cacheptr() ptr +# define Ifileno(f) ((f)->fd) +#else +# define RILE FILE +# define declarecache register FILE *ptr +# define setupcache(f) (ptr = (f)) +# define uncache(f) +# define cache(f) +# define Igeteof_(f,c,s) {if(((c)=getc(f))==EOF){testIerror(f);if(feof(f))s}} +# define cachegeteof_(c,s) Igeteof_(ptr,c,s) +# define Iget_(f,c) { if (((c)=getc(f))==EOF) testIeof(f); } +# define cacheget_(c) Iget_(ptr,c) +# define cacheunget_(f,c) if(fseek(ptr,-2L,SEEK_CUR))Ierror();else cacheget_(c) +# define Ioffset_type long +# define Itell(f) ftell(f) +# define Ifileno(f) fileno(f) +#endif + +/* Print a char, but abort on write error. */ +#define aputc_(c,o) { if (putc(c,o)==EOF) testOerror(o); } + +/* Get a character from an RCS file, perhaps copying to a new RCS file. */ +#define GETCeof_(o,c,s) { cachegeteof_(c,s) if (o) aputc_(c,o) } +#define GETC_(o,c) { cacheget_(c) if (o) aputc_(c,o) } + + +#define WORKMODE(RCSmode, writable) (((RCSmode)&(mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH)) | ((writable)?S_IWUSR:0)) +/* computes mode of working file: same as RCSmode, but write permission */ +/* determined by writable */ + + +/* character classes and token codes */ +enum tokens { +/* classes */ DELIM, DIGIT, IDCHAR, NEWLN, LETTER, Letter, + PERIOD, SBEGIN, SPACE, UNKN, +/* tokens */ COLON, ID, NUM, SEMI, STRING +}; + +#define SDELIM '@' /* the actual character is needed for string handling*/ +/* SDELIM must be consistent with ctab[], so that ctab[SDELIM]==SBEGIN. + * there should be no overlap among SDELIM, KDELIM, and VDELIM + */ + +#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than ctab[c]==DIGIT */ + + + + + +/*************************************** + * Data structures for the symbol table + ***************************************/ + +/* Buffer of arbitrary data */ +struct buf { + char *string; + size_t size; +}; +struct cbuf { + char const *string; + size_t size; +}; + +/* Hash table entry */ +struct hshentry { + char const * num; /* pointer to revision number (ASCIZ) */ + char const * date; /* pointer to date of checkin */ + char const * author; /* login of person checking in */ + char const * lockedby; /* who locks the revision */ + char const * state; /* state of revision (Exp by default) */ + char const * name; /* name (if any) by which retrieved */ + struct cbuf log; /* log message requested at checkin */ + struct branchhead * branches; /* list of first revisions on branches*/ + struct cbuf ig; /* ignored phrases in admin part */ + struct cbuf igtext; /* ignored phrases in deltatext part */ + struct hshentry * next; /* next revision on same branch */ + struct hshentry * nexthsh; /* next revision with same hash value */ + long insertlns;/* lines inserted (computed by rlog) */ + long deletelns;/* lines deleted (computed by rlog) */ + char selector; /* true if selected, false if deleted */ +}; + +/* list of hash entries */ +struct hshentries { + struct hshentries *rest; + struct hshentry *first; +}; + +/* list element for branch lists */ +struct branchhead { + struct hshentry * hsh; + struct branchhead * nextbranch; +}; + +/* accesslist element */ +struct access { + char const * login; + struct access * nextaccess; +}; + +/* list element for locks */ +struct rcslock { + char const * login; + struct hshentry * delta; + struct rcslock * nextlock; +}; + +/* list element for symbolic names */ +struct assoc { + char const * symbol; + char const * num; + struct assoc * nextassoc; +}; + + +#define mainArgs (argc,argv) int argc; char **argv; + +#if RCS_lint +# define libId(name,rcsid) +# define mainProg(name,cmd,rcsid) int name mainArgs +#else +# define libId(name,rcsid) char const name[] = rcsid; +# define mainProg(n,c,i) char const Copyright[] = "Copyright 1982,1988,1989 Walter F. Tichy, Purdue CS\nCopyright 1990,1991,1992,1993,1994,1995 Paul Eggert", baseid[] = RCSBASE, cmdid[] = c; libId(n,i) int main P((int,char**)); int main mainArgs +#endif + +/* + * Markers for keyword expansion (used in co and ident) + * Every byte must have class LETTER or Letter. + */ +#define AUTHOR "Author" +#define DATE "Date" +#define HEADER "Header" +#define IDH "Id" +#define LOCKER "Locker" +#define LOG "Log" +#define NAME "Name" +#define RCSFILE "RCSfile" +#define REVISION "Revision" +#define SOURCE "Source" +#define STATE "State" +#define CVSHEADER "CVSHeader" +#define keylength 9 /* max length of any of the above keywords */ + +enum markers { Nomatch, Author, Date, Header, Id, + Locker, Log, Name, RCSfile, Revision, Source, State, CVSHeader, + LocalId }; + /* This must be in the same order as rcskeys.c's Keyword[] array. */ + +#define DELNUMFORM "\n\n%s\n%s\n" +/* used by putdtext and scanlogtext */ + +#define EMPTYLOG "*** empty log message ***" /* used by ci and rlog */ + +/* main program */ +extern char const cmdid[]; +void exiterr P((void)) exiting; + +/* merge */ +int merge P((int,char const*,char const*const[3],char const*const[3])); + +/* rcsedit */ +#define ciklogsize 23 /* sizeof("checked in with -k by ") */ +extern FILE *fcopy; +extern char const *resultname; +extern char const ciklog[ciklogsize]; +extern int locker_expansion; +RILE *rcswriteopen P((struct buf*,struct stat*,int)); +char const *makedirtemp P((int)); +char const *getcaller P((void)); +int addlock P((struct hshentry*,int)); +int addsymbol P((char const*,char const*,int)); +int checkaccesslist P((void)); +int chnamemod P((FILE**,char const*,char const*,int,mode_t,time_t)); +int donerewrite P((int,time_t)); +int dorewrite P((int,int)); +int expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*,int)); +int findlock P((int,struct hshentry**)); +int setmtime P((char const*,time_t)); +void ORCSclose P((void)); +void ORCSerror P((void)); +void copystring P((void)); +void dirtempunlink P((void)); +void enterstring P((void)); +void finishedit P((struct hshentry const*,FILE*,int)); +void keepdirtemp P((char const*)); +void openfcopy P((FILE*)); +void snapshotedit P((FILE*)); +void xpandstring P((struct hshentry const*)); +#if has_NFS || bad_unlink + int un_link P((char const*)); +#else +# define un_link(s) unlink(s) +#endif +#if large_memory + void edit_string P((void)); +# define editstring(delta) edit_string() +#else + void editstring P((struct hshentry const*)); +#endif + +/* rcsfcmp */ +int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*)); + +/* rcsfnms */ +#define bufautobegin(b) clear_buf(b) +#define clear_buf(b) (VOID ((b)->string = 0, (b)->size = 0)) +extern FILE *workstdout; +extern char *workname; +extern char const *RCSname; +extern char const *suffixes; +extern int fdlock; +extern struct stat RCSstat; +RILE *rcsreadopen P((struct buf*,struct stat*,int)); +char *bufenlarge P((struct buf*,char const**)); +char const *basefilename P((char const*)); +char const *getfullRCSname P((void)); +char const *getfullCVSname P((void)); +char const *maketemp P((int)); +char const *rcssuffix P((char const*)); +int pairnames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int)); +struct cbuf bufremember P((struct buf*,size_t)); +void bufalloc P((struct buf*,size_t)); +void bufautoend P((struct buf*)); +void bufrealloc P((struct buf*,size_t)); +void bufscat P((struct buf*,char const*)); +void bufscpy P((struct buf*,char const*)); +void tempunlink P((void)); + +/* rcsgen */ +extern int interactiveflag; +extern struct buf curlogbuf; +char const *buildrevision P((struct hshentries const*,struct hshentry*,FILE*,int)); +int getcstdin P((void)); +int putdtext P((struct hshentry const*,char const*,FILE*,int)); +int ttystdin P((void)); +int yesorno P((int,char const*,...)) printf_string(2,3); +struct cbuf cleanlogmsg P((char*,size_t)); +struct cbuf getsstdin P((char const*,char const*,char const*,struct buf*)); +void putdesc P((int,char*)); +void putdftext P((struct hshentry const*,RILE*,FILE*,int)); + +/* rcskeep */ +extern int prevkeys; +extern struct buf prevauthor, prevdate, prevname, prevrev, prevstate; +int getoldkeys P((RILE*)); + +/* rcskeys */ +extern char const *Keyword[]; +extern enum markers LocalIdMode; +enum markers trymatch P((char const*)); +void setRCSLocalId(char const *); +void setIncExc(char const *); + +/* rcslex */ +extern FILE *foutptr; +extern FILE *frewrite; +extern RILE *finptr; +extern char const *NextString; +extern enum tokens nexttok; +extern int hshenter; +extern int nerror; +extern int nextc; +extern int quietflag; +extern long rcsline; +char const *getid P((void)); +void efaterror P((char const*)) exiting; +void enfaterror P((int,char const*)) exiting; +void fatcleanup P((int)) exiting; +void faterror P((char const*,...)) printf_string_exiting(1,2); +void fatserror P((char const*,...)) printf_string_exiting(1,2); +void rcsfaterror P((char const*,...)) printf_string_exiting(1,2); +void Ieof P((void)) exiting; +void Ierror P((void)) exiting; +void Oerror P((void)) exiting; +char *checkid P((char*,int)); +char *checksym P((char*,int)); +int eoflex P((void)); +int getkeyopt P((char const*)); +int getlex P((enum tokens)); +struct cbuf getphrases P((char const*)); +struct cbuf savestring P((struct buf*)); +struct hshentry *getnum P((void)); +void Ifclose P((RILE*)); +void Izclose P((RILE**)); +void Lexinit P((void)); +void Ofclose P((FILE*)); +void Orewind P((FILE*)); +void Ozclose P((FILE**)); +void aflush P((FILE*)); +void afputc P((int,FILE*)); +void aprintf P((FILE*,char const*,...)) printf_string(2,3); +void aputs P((char const*,FILE*)); +void checksid P((char*)); +void checkssym P((char*)); +void diagnose P((char const*,...)) printf_string(1,2); +void eerror P((char const*)); +void eflush P((void)); +void enerror P((int,char const*)); +void error P((char const*,...)) printf_string(1,2); +void fvfprintf P((FILE*,char const*,va_list)); +void getkey P((char const*)); +void getkeystring P((char const*)); +void nextlex P((void)); +void oflush P((void)); +void printstring P((void)); +void readstring P((void)); +void redefined P((int)); +void rcserror P((char const*,...)) printf_string(1,2); +void rcswarn P((char const*,...)) printf_string(1,2); +void testIerror P((FILE*)); +void testOerror P((FILE*)); +void warn P((char const*,...)) printf_string(1,2); +void warnignore P((void)); +void workerror P((char const*,...)) printf_string(1,2); +void workwarn P((char const*,...)) printf_string(1,2); +#if has_madvise && has_mmap && large_memory + void advise_access P((RILE*,int)); +# define if_advise_access(p,f,advice) if (p) advise_access(f,advice) +#else +# define advise_access(f,advice) +# define if_advise_access(p,f,advice) +#endif +#if large_memory && maps_memory + RILE *I_open P((char const*,struct stat*)); +# define Iopen(f,m,s) I_open(f,s) +#else + RILE *Iopen P((char const*,char const*,struct stat*)); +#endif +#if !large_memory + void testIeof P((FILE*)); + void Irewind P((RILE*)); +#endif + +/* rcsmap */ +extern enum tokens const ctab[]; + +/* rcsrev */ +char *partialno P((struct buf*,char const*,int)); +char const *namedrev P((char const*,struct hshentry*)); +char const *tiprev P((void)); +int cmpdate P((char const*,char const*)); +int cmpnum P((char const*,char const*)); +int cmpnumfld P((char const*,char const*,int)); +int compartial P((char const*,char const*,int)); +int expandsym P((char const*,struct buf*)); +int fexpandsym P((char const*,struct buf*,RILE*)); +struct hshentry *genrevs P((char const*,char const*,char const*,char const*,struct hshentries**)); +int countnumflds P((char const*)); +void getbranchno P((char const*,struct buf*)); + +/* rcssyn */ +/* These expand modes must agree with Expand_names[] in rcssyn.c. */ +#define KEYVAL_EXPAND 0 /* -kkv `$Keyword: value $' */ +#define KEYVALLOCK_EXPAND 1 /* -kkvl `$Keyword: value locker $' */ +#define KEY_EXPAND 2 /* -kk `$Keyword$' */ +#define VAL_EXPAND 3 /* -kv `value' */ +#define OLD_EXPAND 4 /* -ko use old string, omitting expansion */ +#define BINARY_EXPAND 5 /* -kb like -ko, but use binary mode I/O */ +#define MIN_UNEXPAND OLD_EXPAND /* min value for no logical expansion */ +#define MIN_UNCHANGED_EXPAND (OPEN_O_BINARY ? BINARY_EXPAND : OLD_EXPAND) + /* min value guaranteed to yield an identical file */ +struct diffcmd { + long + line1, /* number of first line */ + nlines, /* number of lines affected */ + adprev, /* previous 'a' line1+1 or 'd' line1 */ + dafter; /* sum of previous 'd' line1 and previous 'd' nlines */ +}; +extern char const * Dbranch; +extern struct access * AccessList; +extern struct assoc * Symbols; +extern struct cbuf Comment; +extern struct cbuf Ignored; +extern struct rcslock *Locks; +extern struct hshentry * Head; +extern int Expand; +extern int StrictLocks; +extern int TotalDeltas; +extern char const *const expand_names[]; +extern char const + Kaccess[], Kauthor[], Kbranch[], Kcomment[], + Kdate[], Kdesc[], Kexpand[], Khead[], Klocks[], Klog[], + Knext[], Kstate[], Kstrict[], Ksymbols[], Ktext[]; +void unexpected_EOF P((void)) exiting; +int getdiffcmd P((RILE*,int,FILE*,struct diffcmd*)); +int str2expmode P((char const*)); +void getadmin P((void)); +void getdesc P((int)); +void gettree P((void)); +void ignorephrases P((char const*)); +void initdiffcmd P((struct diffcmd*)); +void putadmin P((void)); +void putstring P((FILE*,int,struct cbuf,int)); +void puttree P((struct hshentry const*,FILE*)); + +/* rcstime */ +#define zonelenmax 9 /* maxiumum length of time zone string, e.g. "+12:34:56" */ +char const *date2str P((char const[datesize],char[datesize + zonelenmax])); +time_t date2time P((char const[datesize])); +void str2date P((char const*,char[datesize])); +void time2date P((time_t,char[datesize])); +void zone_set P((char const*)); + +/* rcsutil */ +extern int RCSversion; +FILE *fopenSafer P((char const*,char const*)); +char *cgetenv P((char const*)); +char *fstr_save P((char const*)); +char *str_save P((char const*)); +char const *getusername P((int)); +int fdSafer P((int)); +int getRCSINIT P((int,char**,char***)); +int run P((int,char const*,...)); +int runv P((int,char const*,char const**)); +malloc_type fremember P((malloc_type)); +malloc_type ftestalloc P((size_t)); +malloc_type testalloc P((size_t)); +malloc_type testrealloc P((malloc_type,size_t)); +#define ftalloc(T) ftnalloc(T,1) +#define talloc(T) tnalloc(T,1) +#if RCS_lint + extern malloc_type lintalloc; +# define ftnalloc(T,n) (lintalloc = ftestalloc(sizeof(T)*(n)), (T*)0) +# define tnalloc(T,n) (lintalloc = testalloc(sizeof(T)*(n)), (T*)0) +# define trealloc(T,p,n) (lintalloc = testrealloc((malloc_type)0, sizeof(T)*(n)), p) +# define tfree(p) +#else +# define ftnalloc(T,n) ((T*) ftestalloc(sizeof(T)*(n))) +# define tnalloc(T,n) ((T*) testalloc(sizeof(T)*(n))) +# define trealloc(T,p,n) ((T*) testrealloc((malloc_type)(p), sizeof(T)*(n))) +# define tfree(p) free((malloc_type)(p)) +#endif +time_t now P((void)); +void awrite P((char const*,size_t,FILE*)); +void fastcopy P((RILE*,FILE*)); +void ffree P((void)); +void ffree1 P((char const*)); +void setRCSversion P((char const*)); +#if has_signal + void catchints P((void)); + void ignoreints P((void)); + void restoreints P((void)); +#else +# define catchints() +# define ignoreints() +# define restoreints() +#endif +#if has_mmap && large_memory +# if has_NFS && mmap_signal + void catchmmapints P((void)); + void readAccessFilenameBuffer P((char const*,unsigned char const*)); +# else +# define catchmmapints() +# endif +#endif +#if has_getuid + uid_t ruid P((void)); +# define myself(u) ((u) == ruid()) +#else +# define myself(u) true +#endif +#if has_setuid + uid_t euid P((void)); + void nosetid P((void)); + void seteid P((void)); + void setrid P((void)); +#else +# define nosetid() +# define seteid() +# define setrid() +#endif + +/* version */ +extern char const RCS_version_string[]; diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c new file mode 100644 index 00000000000..dc9dd307de6 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsedit.c @@ -0,0 +1,1958 @@ +/* RCS stream editor */ + +/****************************************************************************** + * edits the input file according to a + * script from stdin, generated by diff -n + * performs keyword expansion + ****************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (dirtpname): No longer external. + * (do_link): Simplify logic. + * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for. + * (fopen_update_truncate): Replace `#if' with `if'. + * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x. + * + * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output + * at the end of incomplete lines. + * + * (keyreplace): Do not assume that seeking backwards + * at the start of a file will fail; on some systems it succeeds. + * Convert C- and Pascal-style comment starts to ` *' in comment leader. + * + * (rcswriteopen): Use fdSafer to get safer file descriptor. + * Open RCS file with FOPEN_RB. + * + * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result. + * Fall back on chmod if fchmod fails, since it might be ENOSYS. + * + * (aflush): Move to rcslex.c. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Normally calculate the $Log prefix from context, not from RCS file. + * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint. + * + * Revision 5.16 1993/11/03 17:42:27 eggert + * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails. + * Escape white space, $, and \ in keyword string file names. + * Don't output 2 spaces between date and time after Log. + * + * Revision 5.15 1992/07/28 16:12:44 eggert + * Some hosts have readlink but not ELOOP. Avoid `unsigned'. + * Preserve dates more systematically. Statement macro names now end in _. + * + * Revision 5.14 1992/02/17 23:02:24 eggert + * Add -T support. + * + * Revision 5.13 1992/01/24 18:44:19 eggert + * Add support for bad_chmod_close, bad_creat0. + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * Add setmode parameter to chnamemod. addsymbol now reports changes. + * while (E) ; -> while (E) continue; + * + * Revision 5.11 1991/11/03 01:11:44 eggert + * Move the warning about link breaking to where they're actually being broken. + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Support piece tables even if !has_mmap. Fix rare NFS bugs. + * + * Revision 5.9 1991/09/17 19:07:40 eggert + * SGI readlink() yields ENXIO, not EINVAL, for nonlinks. + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Add piece tables, NFS bug workarounds. Catch odd filenames. Tune. + * + * Revision 5.7 1991/04/21 11:58:21 eggert + * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.6 1991/02/25 07:12:40 eggert + * Fix setuid bug. Support new link behavior. Work around broken "w+" fopen. + * + * Revision 5.5 1990/12/30 05:07:35 eggert + * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL). + * + * Revision 5.4 1990/11/01 05:03:40 eggert + * Permit arbitrary data in comment leaders. + * + * Revision 5.3 1990/09/11 02:41:13 eggert + * Tune expandline(). + * + * Revision 5.2 1990/09/04 08:02:21 eggert + * Count RCS lines better. Improve incomplete line handling. + * + * Revision 5.1 1990/08/29 07:13:56 eggert + * Add -kkvl. + * Fix bug when getting revisions to files ending in incomplete lines. + * Fix bug in comment leader expansion. + * + * Revision 5.0 1990/08/22 08:12:47 eggert + * Don't require final newline. + * Don't append "checked in with -k by " to logs, + * so that checking in a program with -k doesn't change it. + * Don't generate trailing white space for empty comment leader. + * Remove compile-time limits; use malloc instead. Add -k, -V. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. Check diff's output. + * + * Revision 4.8 89/05/01 15:12:35 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.7 88/11/08 13:54:14 narten + * misplaced semicolon caused infinite loop + * + * Revision 4.6 88/08/09 19:12:45 eggert + * Shrink stdio code size; allow cc -R. + * + * Revision 4.5 87/12/18 11:38:46 narten + * Changes from the 43. version. Don't know the significance of the + * first change involving "rewind". Also, additional "lint" cleanup. + * (Guy Harris) + * + * Revision 4.4 87/10/18 10:32:21 narten + * Updating version numbers. Changes relative to version 1.1 actually + * relative to 4.1 + * + * Revision 1.4 87/09/24 13:59:29 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.3 87/09/15 16:39:39 shepler + * added an initializatin of the variables editline and linecorr + * this will be done each time a file is processed. + * (there was an obscure bug where if co was used to retrieve multiple files + * it would dump) + * fix attributed to Roy Morris @FileNet Corp ...!felix!roy + * + * Revision 1.2 87/03/27 14:22:17 jenkins + * Port to suns + * + * Revision 4.1 83/05/12 13:10:30 wft + * Added new markers Id and RCSfile; added locker to Header and Id. + * Overhauled expandline completely() (problem with $01234567890123456789@). + * Moved trymatch() and marker table to rcskeys.c. + * + * Revision 3.7 83/05/12 13:04:39 wft + * Added retry to expandline to resume after failed match which ended in $. + * Fixed truncation problem for $19chars followed by@@. + * Log no longer expands full path of RCS file. + * + * Revision 3.6 83/05/11 16:06:30 wft + * added retry to expandline to resume after failed match which ended in $. + * Fixed truncation problem for $19chars followed by@@. + * + * Revision 3.5 82/12/04 13:20:56 wft + * Added expansion of keyword Locker. + * + * Revision 3.4 82/12/03 12:26:54 wft + * Added line number correction in case editing does not start at the + * beginning of the file. + * Changed keyword expansion to always print a space before closing KDELIM; + * Expansion for Header shortened. + * + * Revision 3.3 82/11/14 14:49:30 wft + * removed Suffix from keyword expansion. Replaced fclose with ffclose. + * keyreplace() gets log message from delta, not from curlogmsg. + * fixed expression overflow in while(c=putc(GETC.... + * checked nil printing. + * + * Revision 3.2 82/10/18 21:13:39 wft + * I added checks for write errors during the co process, and renamed + * expandstring() to xpandstring(). + * + * Revision 3.1 82/10/13 15:52:55 wft + * changed type of result of getc() from char to int. + * made keyword expansion loop in expandline() portable to machines + * without sign-extension. + */ + + +#include "rcsbase.h" + +libId(editId, "$FreeBSD$") + +static void editEndsPrematurely P((void)) exiting; +static void editLineNumberOverflow P((void)) exiting; +static void escape_string P((FILE*,char const*)); +static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int)); + +FILE *fcopy; /* result file descriptor */ +char const *resultname; /* result pathname */ +int locker_expansion; /* should the locker name be appended to Id val? */ +#if !large_memory + static RILE *fedit; /* edit file descriptor */ + static char const *editname; /* edit pathname */ +#endif +static long editline; /* edit line counter; #lines before cursor */ +static long linecorr; /* #adds - #deletes in each edit run. */ + /*used to correct editline in case file is not rewound after */ + /* applying one delta */ + +/* indexes into dirtpname */ +#define lockdirtp_index 0 +#define newRCSdirtp_index bad_creat0 +#define newworkdirtp_index (newRCSdirtp_index+1) +#define DIRTEMPNAMES (newworkdirtp_index + 1) + +enum maker {notmade, real, effective}; +static struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */ +static enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */ +#define lockname (dirtpname[lockdirtp_index].string) +#define newRCSname (dirtpname[newRCSdirtp_index].string) + + +#if has_NFS || bad_unlink + int +un_link(s) + char const *s; +/* + * Remove S, even if it is unwritable. + * Ignore unlink() ENOENT failures; NFS generates bogus ones. + */ +{ +# if bad_unlink + if (unlink(s) == 0) + return 0; + else { + int e = errno; + /* + * Forge ahead even if errno == ENOENT; some completely + * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT + * even for existing unwritable files. + */ + if (chmod(s, S_IWUSR) != 0) { + errno = e; + return -1; + } + } +# endif +# if has_NFS + return unlink(s)==0 || errno==ENOENT ? 0 : -1; +# else + return unlink(s); +# endif +} +#endif + +#if !has_rename +# if !has_NFS +# define do_link(s,t) link(s,t) +# else + static int do_link P((char const*,char const*)); + static int +do_link(s, t) + char const *s, *t; +/* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */ +{ + int r = link(s, t); + + if (r != 0 && errno == EEXIST) { + struct stat sb, tb; + if ( + stat(s, &sb) == 0 && + stat(t, &tb) == 0 && + same_file(sb, tb, 0) + ) + r = 0; + errno = EEXIST; + } + return r; +} +# endif +#endif + + + static void +editEndsPrematurely() +{ + fatserror("edit script ends prematurely"); +} + + static void +editLineNumberOverflow() +{ + fatserror("edit script refers to line past end of file"); +} + + +#if large_memory + +#if has_memmove +# define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type)) +#else + static void movelines P((Iptr_type*,Iptr_type const*,long)); + static void +movelines(s1, s2, n) + register Iptr_type *s1; + register Iptr_type const *s2; + register long n; +{ + if (s1 < s2) + do { + *s1++ = *s2++; + } while (--n); + else { + s1 += n; + s2 += n; + do { + *--s1 = *--s2; + } while (--n); + } +} +#endif + +static void deletelines P((long,long)); +static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*)); +static void insertline P((long,Iptr_type)); +static void snapshotline P((FILE*,Iptr_type)); + +/* + * `line' contains pointers to the lines in the currently `edited' file. + * It is a 0-origin array that represents linelim-gapsize lines. + * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines. + * line[gap .. gap+gapsize-1] contains garbage. + * + * Any @s in lines are duplicated. + * Lines are terminated by \n, or (for a last partial line only) by single @. + */ +static Iptr_type *line; +static size_t gap, gapsize, linelim; + + static void +insertline(n, l) + long n; + Iptr_type l; +/* Before line N, insert line L. N is 0-origin. */ +{ + if (linelim-gapsize < n) + editLineNumberOverflow(); + if (!gapsize) + line = + !linelim ? + tnalloc(Iptr_type, linelim = gapsize = 1024) + : ( + gap = gapsize = linelim, + trealloc(Iptr_type, line, linelim <<= 1) + ); + if (n < gap) + movelines(line+n+gapsize, line+n, gap-n); + else if (gap < n) + movelines(line+gap, line+gap+gapsize, n-gap); + + line[n] = l; + gap = n + 1; + gapsize--; +} + + static void +deletelines(n, nlines) + long n, nlines; +/* Delete lines N through N+NLINES-1. N is 0-origin. */ +{ + long l = n + nlines; + if (linelim-gapsize < l || l < n) + editLineNumberOverflow(); + if (l < gap) + movelines(line+l+gapsize, line+l, gap-l); + else if (gap < n) + movelines(line+gap, line+gap+gapsize, n-gap); + + gap = n; + gapsize += nlines; +} + + static void +snapshotline(f, l) + register FILE *f; + register Iptr_type l; +{ + register int c; + do { + if ((c = *l++) == SDELIM && *l++ != SDELIM) + return; + aputc_(c, f) + } while (c != '\n'); +} + + void +snapshotedit(f) + FILE *f; +/* Copy the current state of the edits to F. */ +{ + register Iptr_type *p, *lim, *l=line; + for (p=l, lim=l+gap; pptr = l; + if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0) + faterror("finisheditline internal error"); +} + + void +finishedit(delta, outfile, done) + struct hshentry const *delta; + FILE *outfile; + int done; +/* + * Doing expansion if DELTA is set, output the state of the edits to OUTFILE. + * But do nothing unless DONE is set (which means we are on the last pass). + */ +{ + if (done) { + openfcopy(outfile); + outfile = fcopy; + if (!delta) + snapshotedit(outfile); + else { + register Iptr_type *p, *lim, *l = line; + register RILE *fin = finptr; + Iptr_type here = fin->ptr; + for (p=l, lim=l+gap; pptr = here; + } + } +} + +/* Open a temporary NAME for output, truncating any previous contents. */ +# define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK) +#else /* !large_memory */ + static FILE * fopen_update_truncate P((char const*)); + static FILE * +fopen_update_truncate(name) + char const *name; +{ + if (bad_fopen_wplus && un_link(name) != 0) + efaterror(name); + return fopenSafer(name, FOPEN_WPLUS_WORK); +} +#endif + + + void +openfcopy(f) + FILE *f; +{ + if (!(fcopy = f)) { + if (!resultname) + resultname = maketemp(2); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); + } +} + + +#if !large_memory + + static void swapeditfiles P((FILE*)); + static void +swapeditfiles(outfile) + FILE *outfile; +/* Function: swaps resultname and editname, assigns fedit=fcopy, + * and rewinds fedit for reading. Set fcopy to outfile if nonnull; + * otherwise, set fcopy to be resultname opened for reading and writing. + */ +{ + char const *tmpptr; + + editline = 0; linecorr = 0; + Orewind(fcopy); + fedit = fcopy; + tmpptr=editname; editname=resultname; resultname=tmpptr; + openfcopy(outfile); +} + + void +snapshotedit(f) + FILE *f; +/* Copy the current state of the edits to F. */ +{ + finishedit((struct hshentry *)0, (FILE*)0, false); + fastcopy(fedit, f); + Irewind(fedit); +} + + void +finishedit(delta, outfile, done) + struct hshentry const *delta; + FILE *outfile; + int done; +/* copy the rest of the edit file and close it (if it exists). + * if delta, perform keyword substitution at the same time. + * If DONE is set, we are finishing the last pass. + */ +{ + register RILE *fe; + register FILE *fc; + + fe = fedit; + if (fe) { + fc = fcopy; + if (delta) { + while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) + ; + } else { + fastcopy(fe,fc); + } + Ifclose(fe); + } + if (!done) + swapeditfiles(outfile); +} +#endif + + + +#if large_memory +# define copylines(upto,delta) (editline = (upto)) +#else + static void copylines P((long,struct hshentry const*)); + static void +copylines(upto, delta) + register long upto; + struct hshentry const *delta; +/* + * Copy input lines editline+1..upto from fedit to fcopy. + * If delta, keyword expansion is done simultaneously. + * editline is updated. Rewinds a file only if necessary. + */ +{ + register int c; + declarecache; + register FILE *fc; + register RILE *fe; + + if (upto < editline) { + /* swap files */ + finishedit((struct hshentry *)0, (FILE*)0, false); + /* assumes edit only during last pass, from the beginning*/ + } + fe = fedit; + fc = fcopy; + if (editline < upto) + if (delta) + do { + if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1) + editLineNumberOverflow(); + } while (++editline < upto); + else { + setupcache(fe); cache(fe); + do { + do { + cachegeteof_(c, editLineNumberOverflow();) + aputc_(c, fc) + } while (c != '\n'); + } while (++editline < upto); + uncache(fe); + } +} +#endif + + + + void +xpandstring(delta) + struct hshentry const *delta; +/* Function: Reads a string terminated by SDELIM from finptr and writes it + * to fcopy. Double SDELIM is replaced with single SDELIM. + * Keyword expansion is performed with data from delta. + * If foutptr is nonnull, the string is also copied unchanged to foutptr. + */ +{ + while (1 < expandline(finptr,fcopy,delta,true,foutptr,true)) + continue; +} + + + void +copystring() +/* Function: copies a string terminated with a single SDELIM from finptr to + * fcopy, replacing all double SDELIM with a single SDELIM. + * If foutptr is nonnull, the string also copied unchanged to foutptr. + * editline is incremented by the number of lines copied. + * Assumption: next character read is first string character. + */ +{ register c; + declarecache; + register FILE *frew, *fcop; + register int amidline; + register RILE *fin; + + fin = finptr; + setupcache(fin); cache(fin); + frew = foutptr; + fcop = fcopy; + amidline = false; + for (;;) { + GETC_(frew,c) + switch (c) { + case '\n': + ++editline; + ++rcsline; + amidline = false; + break; + case SDELIM: + GETC_(frew,c) + if (c != SDELIM) { + /* end of string */ + nextc = c; + editline += amidline; + uncache(fin); + return; + } + /* fall into */ + default: + amidline = true; + break; + } + aputc_(c,fcop) + } +} + + + void +enterstring() +/* Like copystring, except the string is put into the edit data structure. */ +{ +#if !large_memory + editname = 0; + fedit = 0; + editline = linecorr = 0; + resultname = maketemp(1); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); + copystring(); +#else + register int c; + declarecache; + register FILE *frew; + register long e, oe; + register int amidline, oamidline; + register Iptr_type optr; + register RILE *fin; + + e = 0; + gap = 0; + gapsize = linelim; + fin = finptr; + setupcache(fin); cache(fin); + advise_access(fin, MADV_NORMAL); + frew = foutptr; + amidline = false; + for (;;) { + optr = cacheptr(); + GETC_(frew,c) + oamidline = amidline; + oe = e; + switch (c) { + case '\n': + ++e; + ++rcsline; + amidline = false; + break; + case SDELIM: + GETC_(frew,c) + if (c != SDELIM) { + /* end of string */ + nextc = c; + editline = e + amidline; + linecorr = 0; + uncache(fin); + return; + } + /* fall into */ + default: + amidline = true; + break; + } + if (!oamidline) + insertline(oe, optr); + } +#endif +} + + + + + void +#if large_memory +edit_string() +#else + editstring(delta) + struct hshentry const *delta; +#endif +/* + * Read an edit script from finptr and applies it to the edit file. +#if !large_memory + * The result is written to fcopy. + * If delta, keyword expansion is performed simultaneously. + * If running out of lines in fedit, fedit and fcopy are swapped. + * editname is the name of the file that goes with fedit. +#endif + * If foutptr is set, the edit script is also copied verbatim to foutptr. + * Assumes that all these files are open. + * resultname is the name of the file that goes with fcopy. + * Assumes the next input character from finptr is the first character of + * the edit script. Resets nextc on exit. + */ +{ + int ed; /* editor command */ + register int c; + declarecache; + register FILE *frew; +# if !large_memory + register FILE *f; + long line_lim = LONG_MAX; + register RILE *fe; +# endif + register long i; + register RILE *fin; +# if large_memory + register long j; +# endif + struct diffcmd dc; + + editline += linecorr; linecorr=0; /*correct line number*/ + frew = foutptr; + fin = finptr; + setupcache(fin); + initdiffcmd(&dc); + while (0 <= (ed = getdiffcmd(fin,true,frew,&dc))) +#if !large_memory + if (line_lim <= dc.line1) + editLineNumberOverflow(); + else +#endif + if (!ed) { + copylines(dc.line1-1, delta); + /* skip over unwanted lines */ + i = dc.nlines; + linecorr -= i; + editline += i; +# if large_memory + deletelines(editline+linecorr, i); +# else + fe = fedit; + do { + /*skip next line*/ + do { + Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ) + } while (c != '\n'); + } while (--i); +# endif + } else { + /* Copy lines without deleting any. */ + copylines(dc.line1, delta); + i = dc.nlines; +# if large_memory + j = editline+linecorr; +# endif + linecorr += i; +#if !large_memory + f = fcopy; + if (delta) + do { + switch (expandline(fin,f,delta,true,frew,true)){ + case 0: case 1: + if (i==1) + return; + /* fall into */ + case -1: + editEndsPrematurely(); + } + } while (--i); + else +#endif + { + cache(fin); + do { +# if large_memory + insertline(j++, cacheptr()); +# endif + for (;;) { + GETC_(frew, c) + if (c==SDELIM) { + GETC_(frew, c) + if (c!=SDELIM) { + if (--i) + editEndsPrematurely(); + nextc = c; + uncache(fin); + return; + } + } +# if !large_memory + aputc_(c, f) +# endif + if (c == '\n') + break; + } + ++rcsline; + } while (--i); + uncache(fin); + } + } +} + + + +/* The rest is for keyword expansion */ + + + + int +expandline(infile, outfile, delta, delimstuffed, frewfile, dolog) + RILE *infile; + FILE *outfile, *frewfile; + struct hshentry const *delta; + int delimstuffed, dolog; +/* + * Read a line from INFILE and write it to OUTFILE. + * Do keyword expansion with data from DELTA. + * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM. + * If FREWFILE is set, copy the line unchanged to FREWFILE. + * DELIMSTUFFED must be true if FREWFILE is set. + * Append revision history to log only if DOLOG is set. + * Yields -1 if no data is copied, 0 if an incomplete line is copied, + * 2 if a complete line is copied; adds 1 to yield if expansion occurred. + */ +{ + register c; + declarecache; + register FILE *out, *frew; + register char * tp; + register int e, ds, r; + char const *tlim; + static struct buf keyval; + enum markers matchresult; + + setupcache(infile); cache(infile); + out = outfile; + frew = frewfile; + ds = delimstuffed; + bufalloc(&keyval, keylength+3); + e = 0; + r = -1; + + for (;;) { + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto uncache_exit;) + for (;;) { + switch (c) { + case SDELIM: + if (ds) { + GETC_(frew, c) + if (c != SDELIM) { + /* end of string */ + nextc=c; + goto uncache_exit; + } + } + /* fall into */ + default: + aputc_(c,out) + r = 0; + break; + + case '\n': + rcsline += ds; + aputc_(c,out) + r = 2; + goto uncache_exit; + + case KDELIM: + r = 0; + /* check for keyword */ + /* first, copy a long enough string into keystring */ + tp = keyval.string; + *tp++ = KDELIM; + for (;;) { + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto keystring_eof;) + if (tp <= &keyval.string[keylength]) + switch (ctab[c]) { + case LETTER: case Letter: + *tp++ = c; + continue; + default: + break; + } + break; + } + *tp++ = c; *tp = '\0'; + matchresult = trymatch(keyval.string+1); + if (matchresult==Nomatch) { + tp[-1] = 0; + aputs(keyval.string, out); + continue; /* last c handled properly */ + } + + /* Now we have a keyword terminated with a K/VDELIM */ + if (c==VDELIM) { + /* try to find closing KDELIM, and replace value */ + tlim = keyval.string + keyval.size; + for (;;) { + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto keystring_eof;) + if (c=='\n' || c==KDELIM) + break; + *tp++ =c; + if (tlim <= tp) + tp = bufenlarge(&keyval, &tlim); + if (c==SDELIM && ds) { /*skip next SDELIM */ + GETC_(frew, c) + if (c != SDELIM) { + /* end of string before closing KDELIM or newline */ + nextc = c; + goto keystring_eof; + } + } + } + if (c!=KDELIM) { + /* couldn't find closing KDELIM -- give up */ + *tp = 0; + aputs(keyval.string, out); + continue; /* last c handled properly */ + } + } + /* now put out the new keyword value */ + uncache(infile); + keyreplace(matchresult, delta, ds, infile, out, dolog); + cache(infile); + e = 1; + break; + } + break; + } + } + + keystring_eof: + *tp = 0; + aputs(keyval.string, out); + uncache_exit: + uncache(infile); + return r + e; +} + + + static void +escape_string(out, s) + register FILE *out; + register char const *s; +/* Output to OUT the string S, escaping chars that would break `ci -k'. */ +{ + register char c; + for (;;) + switch ((c = *s++)) { + case 0: return; + case '\t': aputs("\\t", out); break; + case '\n': aputs("\\n", out); break; + case ' ': aputs("\\040", out); break; + case KDELIM: aputs("\\044", out); break; + case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;} + /* fall into */ + default: aputc_(c, out) break; + } +} + +char const ciklog[ciklogsize] = "checked in with -k by "; + + static void +keyreplace(marker, delta, delimstuffed, infile, out, dolog) + enum markers marker; + register struct hshentry const *delta; + int delimstuffed; + RILE *infile; + register FILE *out; + int dolog; +/* function: outputs the keyword value(s) corresponding to marker. + * Attributes are derived from delta. + */ +{ + register char const *sp, *cp, *date; + register int c; + register size_t cs, cw, ls; + char const *sp1; + char datebuf[datesize + zonelenmax]; + int RCSv; + int exp; + + sp = Keyword[(int)marker]; + exp = Expand; + date = delta->date; + RCSv = RCSversion; + + if (exp != VAL_EXPAND) + aprintf(out, "%c%s", KDELIM, sp); + if (exp != KEY_EXPAND) { + + if (exp != VAL_EXPAND) + aprintf(out, "%c%c", VDELIM, + marker==Log && RCSvauthor, out); + break; + case Date: + aputs(date2str(date,datebuf), out); + break; + case Id: + case LocalId: + case Header: + case CVSHeader: + if (marker == Id || RCSv < VERSION(4) || + (marker == LocalId && LocalIdMode == Id)) + escape_string(out, basefilename(RCSname)); + else if (marker == CVSHeader || + (marker == LocalId && LocalIdMode == CVSHeader)) + escape_string(out, getfullCVSname()); + else + escape_string(out, getfullRCSname()); + aprintf(out, " %s %s %s %s", + delta->num, + date2str(date, datebuf), + delta->author, + RCSv==VERSION(3) && delta->lockedby ? "Locked" + : delta->state + ); + if (delta->lockedby) + if (VERSION(5) <= RCSv) { + if (locker_expansion || exp==KEYVALLOCK_EXPAND) + aprintf(out, " %s", delta->lockedby); + } else if (RCSv == VERSION(4)) + aprintf(out, " Locker: %s", delta->lockedby); + break; + case Locker: + if (delta->lockedby) + if ( + locker_expansion + || exp == KEYVALLOCK_EXPAND + || RCSv <= VERSION(4) + ) + aputs(delta->lockedby, out); + break; + case Log: + case RCSfile: + escape_string(out, basefilename(RCSname)); + break; + case Name: + if (delta->name) + aputs(delta->name, out); + break; + case Revision: + aputs(delta->num, out); + break; + case Source: + escape_string(out, getfullRCSname()); + break; + case State: + aputs(delta->state, out); + break; + default: + break; + } + if (exp != VAL_EXPAND) + afputc(' ', out); + } + if (exp != VAL_EXPAND) + afputc(KDELIM, out); + + if (marker == Log && dolog) { + struct buf leader; + + sp = delta->log.string; + ls = delta->log.size; + if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1)) + return; + bufautobegin(&leader); + if (RCSversion < VERSION(5)) { + cp = Comment.string; + cs = Comment.size; + } else { + int kdelim_found = 0; + Ioffset_type chars_read = Itell(infile); + declarecache; + setupcache(infile); cache(infile); + + c = 0; /* Pacify `gcc -Wall'. */ + + /* + * Back up to the start of the current input line, + * setting CS to the number of characters before `$Log'. + */ + cs = 0; + for (;;) { + if (!--chars_read) + goto done_backing_up; + cacheunget_(infile, c) + if (c == '\n') + break; + if (c == SDELIM && delimstuffed) { + if (!--chars_read) + break; + cacheunget_(infile, c) + if (c != SDELIM) { + cacheget_(c) + break; + } + } + cs += kdelim_found; + kdelim_found |= c==KDELIM; + } + cacheget_(c) + done_backing_up:; + + /* Copy characters before `$Log' into LEADER. */ + bufalloc(&leader, cs); + cp = leader.string; + for (cw = 0; cw < cs; cw++) { + leader.string[cw] = c; + if (c == SDELIM && delimstuffed) + cacheget_(c) + cacheget_(c) + } + + /* Convert traditional C or Pascal leader to ` *'. */ + for (cw = 0; cw < cs; cw++) + if (ctab[(unsigned char) cp[cw]] != SPACE) + break; + if ( + cw+1 < cs + && cp[cw+1] == '*' + && (cp[cw] == '/' || cp[cw] == '(') + ) { + size_t i = cw+1; + for (;;) + if (++i == cs) { + warn( + "`%c* $Log' is obsolescent; use ` * $Log'.", + cp[cw] + ); + leader.string[cw] = ' '; + break; + } else if (ctab[(unsigned char) cp[i]] != SPACE) + break; + } + + /* Skip `$Log ... $' string. */ + do { + cacheget_(c) + } while (c != KDELIM); + uncache(infile); + } + afputc('\n', out); + awrite(cp, cs, out); + sp1 = date2str(date, datebuf); + if (VERSION(5) <= RCSv) { + aprintf(out, "Revision %s %s %s", + delta->num, sp1, delta->author + ); + } else { + /* oddity: 2 spaces between date and time, not 1 as usual */ + sp1 = strchr(sp1, ' '); + aprintf(out, "Revision %s %.*s %s %s", + delta->num, (int)(sp1-datebuf), datebuf, sp1, + delta->author + ); + } + /* Do not include state: it may change and is not updated. */ + cw = cs; + if (VERSION(5) <= RCSv) + for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) + continue; + for (;;) { + afputc('\n', out); + awrite(cp, cw, out); + if (!ls) + break; + --ls; + c = *sp++; + if (c != '\n') { + awrite(cp+cw, cs-cw, out); + do { + afputc(c,out); + if (!ls) + break; + --ls; + c = *sp++; + } while (c != '\n'); + } + } + bufautoend(&leader); + } +} + +#if has_readlink + static int resolve_symlink P((struct buf*)); + static int +resolve_symlink(L) + struct buf *L; +/* + * If L is a symbolic link, resolve it to the name that it points to. + * If unsuccessful, set errno and yield -1. + * If it points to an existing file, yield 1. + * Otherwise, set errno=ENOENT and yield 0. + */ +{ + char *b, a[SIZEABLE_PATH]; + int e; + size_t s; + ssize_t r; + struct buf bigbuf; + int linkcount = MAXSYMLINKS; + + b = a; + s = sizeof(a); + bufautobegin(&bigbuf); + while ((r = readlink(L->string,b,s)) != -1) + if (r == s) { + bufalloc(&bigbuf, s<<1); + b = bigbuf.string; + s = bigbuf.size; + } else if (!linkcount--) { +# ifndef ELOOP + /* + * Some pedantic Posix 1003.1-1990 hosts have readlink + * but not ELOOP. Approximate ELOOP with EMLINK. + */ +# define ELOOP EMLINK +# endif + errno = ELOOP; + return -1; + } else { + /* Splice symbolic link into L. */ + b[r] = '\0'; + L->string[ + ROOTPATH(b) ? 0 : basefilename(L->string) - L->string + ] = '\0'; + bufscat(L, b); + } + e = errno; + bufautoend(&bigbuf); + errno = e; + switch (e) { + case readlink_isreg_errno: return 1; + case ENOENT: return 0; + default: return -1; + } +} +#endif + + RILE * +rcswriteopen(RCSbuf, status, mustread) + struct buf *RCSbuf; + struct stat *status; + int mustread; +/* + * Create the lock file corresponding to RCSBUF. + * Then try to open RCSBUF for reading and yield its RILE* descriptor. + * Put its status into *STATUS too. + * MUSTREAD is true if the file must already exist, too. + * If all goes well, discard any previously acquired locks, + * and set fdlock to the file descriptor of the RCS lockfile. + */ +{ + register char *tp; + register char const *sp, *RCSpath, *x; + RILE *f; + size_t l; + int e, exists, fdesc, fdescSafer, r, waslocked; + struct buf *dirt; + struct stat statbuf; + + waslocked = 0 <= fdlock; + exists = +# if has_readlink + resolve_symlink(RCSbuf); +# else + stat(RCSbuf->string, &statbuf) == 0 ? 1 + : errno==ENOENT ? 0 : -1; +# endif + if (exists < (mustread|waslocked)) + /* + * There's an unusual problem with the RCS file; + * or the RCS file doesn't exist, + * and we must read or we already have a lock elsewhere. + */ + return 0; + + RCSpath = RCSbuf->string; + sp = basefilename(RCSpath); + l = sp - RCSpath; + dirt = &dirtpname[waslocked]; + bufscpy(dirt, RCSpath); + tp = dirt->string + l; + x = rcssuffix(RCSpath); +# if has_readlink + if (!x) { + error("symbolic link to non RCS file `%s'", RCSpath); + errno = EINVAL; + return 0; + } +# endif + if (*sp == *x) { + error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); + errno = EINVAL; + return 0; + } + /* Create a lock filename that is a function of the RCS filename. */ + if (*x) { + /* + * The suffix is nonempty. + * The lock filename is the first char of of the suffix, + * followed by the RCS filename with last char removed. E.g.: + * foo,v RCS filename with suffix ,v + * ,foo, lock filename + */ + *tp++ = *x; + while (*sp) + *tp++ = *sp++; + *--tp = 0; + } else { + /* + * The suffix is empty. + * The lock filename is the RCS filename + * with last char replaced by '_'. + */ + while ((*tp++ = *sp++)) + continue; + tp -= 2; + if (*tp == '_') { + error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); + errno = EINVAL; + return 0; + } + *tp = '_'; + } + + sp = dirt->string; + + f = 0; + + /* + * good news: + * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY) + * is atomic according to Posix 1003.1-1990. + * bad news: + * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990. + * good news: + * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity + * even with NFS. + * bad news: + * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't + * guarantee atomicity. + * good news: + * Root-over-the-wire NFS access is rare for security reasons. + * This bug has never been reported in practice with RCS. + * So we don't worry about this bug. + * + * An even rarer NFS bug can occur when clients retry requests. + * This can happen in the usual case of NFS over UDP. + * Suppose client A releases a lock by renaming ",f," to "f,v" at + * about the same time that client B obtains a lock by creating ",f,", + * and suppose A's first rename request is delayed, so A reissues it. + * The sequence of events might be: + * A sends rename(",f,", "f,v") + * B sends create(",f,") + * A sends retry of rename(",f,", "f,v") + * server receives, does, and acknowledges A's first rename() + * A receives acknowledgment, and its RCS program exits + * server receives, does, and acknowledges B's create() + * server receives, does, and acknowledges A's retry of rename() + * This not only wrongly deletes B's lock, it removes the RCS file! + * Most NFS implementations have idempotency caches that usually prevent + * this scenario, but such caches are finite and can be overrun. + * This problem afflicts not only RCS, which uses open() and rename() + * to get and release locks; it also afflicts the traditional + * Unix method of using link() and unlink() to get and release locks, + * and the less traditional method of using mkdir() and rmdir(). + * There is no easy workaround. + * Any new method based on lockf() seemingly would be incompatible with + * the old methods; besides, lockf() is notoriously buggy under NFS. + * Since this problem afflicts scads of Unix programs, but is so rare + * that nobody seems to be worried about it, we won't worry either. + */ +# if !open_can_creat +# define create(f) creat(f, OPEN_CREAT_READONLY) +# else +# define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY) +# endif + + catchints(); + ignoreints(); + + /* + * Create a lock file for an RCS file. This should be atomic, i.e. + * if two processes try it simultaneously, at most one should succeed. + */ + seteid(); + fdesc = create(sp); + fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ + e = errno; + setrid(); + + if (0 <= fdesc) + dirtpmaker[0] = effective; + + if (fdescSafer < 0) { + if (e == EACCES && stat(sp,&statbuf) == 0) + /* The RCS file is busy. */ + e = EEXIST; + } else { + e = ENOENT; + if (exists) { + f = Iopen(RCSpath, FOPEN_RB, status); + e = errno; + if (f && waslocked) { + /* Discard the previous lock in favor of this one. */ + ORCSclose(); + seteid(); + r = un_link(lockname); + e = errno; + setrid(); + if (r != 0) + enfaterror(e, lockname); + bufscpy(&dirtpname[lockdirtp_index], sp); + } + } + fdlock = fdescSafer; + } + + restoreints(); + + errno = e; + return f; +} + + void +keepdirtemp(name) + char const *name; +/* Do not unlink name, either because it's not there any more, + * or because it has already been unlinked. + */ +{ + register int i; + for (i=DIRTEMPNAMES; 0<=--i; ) + if (dirtpname[i].string == name) { + dirtpmaker[i] = notmade; + return; + } + faterror("keepdirtemp"); +} + + char const * +makedirtemp(isworkfile) + int isworkfile; +/* + * Create a unique pathname and store it into dirtpname. + * Because of storage in tpnames, dirtempunlink() can unlink the file later. + * Return a pointer to the pathname created. + * If ISWORKFILE is 1, put it into the working file's directory; + * if 0, put the unique file in RCSfile's directory. + */ +{ + register char *tp, *np; + register size_t dl; + register struct buf *bn; + register char const *name = isworkfile ? workname : RCSname; +# if has_mktemp + int fd; +# endif + + dl = basefilename(name) - name; + bn = &dirtpname[newRCSdirtp_index + isworkfile]; + bufalloc(bn, +# if has_mktemp + dl + 9 +# else + strlen(name) + 3 +# endif + ); + bufscpy(bn, name); + np = tp = bn->string; + tp += dl; + *tp++ = '_'; + *tp++ = '0'+isworkfile; + catchints(); +# if has_mktemp + VOID strcpy(tp, "XXXXXX"); + fd = mkstemp(np); + if (fd < 0 || !*np) + faterror("can't make temporary pathname `%.*s_%cXXXXXX'", + (int)dl, name, '0'+isworkfile + ); + close(fd); +# else + /* + * Posix 1003.1-1990 has no reliable way + * to create a unique file in a named directory. + * We fudge here. If the filename is abcde, + * the temp filename is _Ncde where N is a digit. + */ + name += dl; + if (*name) name++; + if (*name) name++; + VOID strcpy(tp, name); +# endif + dirtpmaker[newRCSdirtp_index + isworkfile] = real; + return np; +} + + void +dirtempunlink() +/* Clean up makedirtemp() files. May be invoked by signal handler. */ +{ + register int i; + enum maker m; + + for (i = DIRTEMPNAMES; 0 <= --i; ) + if ((m = dirtpmaker[i]) != notmade) { + if (m == effective) + seteid(); + VOID un_link(dirtpname[i].string); + if (m == effective) + setrid(); + dirtpmaker[i] = notmade; + } +} + + + int +#if has_prototypes +chnamemod( + FILE **fromp, char const *from, char const *to, + int set_mode, mode_t mode, time_t mtime +) + /* The `#if has_prototypes' is needed because mode_t might promote to int. */ +#else + chnamemod(fromp, from, to, set_mode, mode, mtime) + FILE **fromp; char const *from,*to; + int set_mode; mode_t mode; time_t mtime; +#endif +/* + * Rename a file (with stream pointer *FROMP) from FROM to TO. + * FROM already exists. + * If 0 < SET_MODE, change the mode to MODE, before renaming if possible. + * If MTIME is not -1, change its mtime to MTIME before renaming. + * Close and clear *FROMP before renaming it. + * Unlink TO if it already exists. + * Return -1 on error (setting errno), 0 otherwise. + */ +{ + mode_t mode_while_renaming = mode; + int fchmod_set_mode = 0; + +# if bad_a_rename || bad_NFS_rename + struct stat st; + if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) { + if (fstat(fileno(*fromp), &st) != 0) + return -1; + if (bad_a_rename && set_mode <= 0) + mode = st.st_mode; + } +# endif + +# if bad_a_rename + /* + * There's a short window of inconsistency + * during which the lock file is writable. + */ + mode_while_renaming = mode|S_IWUSR; + if (mode != mode_while_renaming) + set_mode = 1; +# endif + +# if has_fchmod + if (0nextlock) + if (strcmp(getcaller(), next->login) == 0) { + if (found) { + rcserror("multiple revisions locked by %s; please specify one", getcaller()); + return 2; + } + found = trail; + } + if (!found) + return 0; + next = *found; + *target = next->delta; + if (delete) { + next->delta->lockedby = 0; + *found = next->nextlock; + } + return 1; +} + + int +addlock(delta, verbose) + struct hshentry * delta; + int verbose; +/* + * Add a lock held by caller to DELTA and yield 1 if successful. + * Print an error message if verbose and yield -1 if no lock is added because + * DELTA is locked by somebody other than caller. + * Return 0 if the caller already holds the lock. + */ +{ + register struct rcslock *next; + + for (next = Locks; next; next = next->nextlock) + if (cmpnum(delta->num, next->delta->num) == 0) + if (strcmp(getcaller(), next->login) == 0) + return 0; + else { + if (verbose) + rcserror("Revision %s is already locked by %s.", + delta->num, next->login + ); + return -1; + } + next = ftalloc(struct rcslock); + delta->lockedby = next->login = getcaller(); + next->delta = delta; + next->nextlock = Locks; + Locks = next; + return 1; +} + + + int +addsymbol(num, name, rebind) + char const *num, *name; + int rebind; +/* + * Associate with revision NUM the new symbolic NAME. + * If NAME already exists and REBIND is set, associate NAME with NUM; + * otherwise, print an error message and return false; + * Return -1 if unsuccessful, 0 if no change, 1 if change. + */ +{ + register struct assoc *next; + + for (next = Symbols; next; next = next->nextassoc) + if (strcmp(name, next->symbol) == 0) + if (strcmp(next->num,num) == 0) + return 0; + else if (rebind) { + next->num = num; + return 1; + } else { + rcserror("symbolic name %s already bound to %s", + name, next->num + ); + return -1; + } + next = ftalloc(struct assoc); + next->symbol = name; + next->num = num; + next->nextassoc = Symbols; + Symbols = next; + return 1; +} + + + + char const * +getcaller() +/* Get the caller's login name. */ +{ +# if has_setuid + return getusername(euid()!=ruid()); +# else + return getusername(false); +# endif +} + + + int +checkaccesslist() +/* + * Return true if caller is the superuser, the owner of the + * file, the access list is empty, or caller is on the access list. + * Otherwise, print an error message and return false. + */ +{ + register struct access const *next; + + if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0) + return true; + + next = AccessList; + do { + if (strcmp(getcaller(), next->login) == 0) + return true; + } while ((next = next->nextaccess)); + + rcserror("user %s not on the access list", getcaller()); + return false; +} + + + int +dorewrite(lockflag, changed) + int lockflag, changed; +/* + * Do nothing if LOCKFLAG is zero. + * Prepare to rewrite an RCS file if CHANGED is positive. + * Stop rewriting if CHANGED is zero, because there won't be any changes. + * Fail if CHANGED is negative. + * Return 0 on success, -1 on failure. + */ +{ + int r = 0, e; + + if (lockflag) + if (changed) { + if (changed < 0) + return -1; + putadmin(); + puttree(Head, frewrite); + aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); + foutptr = frewrite; + } else { +# if bad_creat0 + int nr = !!frewrite, ne = 0; +# endif + ORCSclose(); + seteid(); + ignoreints(); +# if bad_creat0 + if (nr) { + nr = un_link(newRCSname); + ne = errno; + keepdirtemp(newRCSname); + } +# endif + r = un_link(lockname); + e = errno; + keepdirtemp(lockname); + restoreints(); + setrid(); + if (r != 0) + enerror(e, lockname); +# if bad_creat0 + if (nr != 0) { + enerror(ne, newRCSname); + r = -1; + } +# endif + } + return r; +} + + int +donerewrite(changed, newRCStime) + int changed; + time_t newRCStime; +/* + * Finish rewriting an RCS file if CHANGED is nonzero. + * Set its mode if CHANGED is positive. + * Set its modification time to NEWRCSTIME unless it is -1. + * Return 0 on success, -1 on failure. + */ +{ + int r = 0, e = 0; +# if bad_creat0 + int lr, le; +# endif + + if (changed && !nerror) { + if (finptr) { + fastcopy(finptr, frewrite); + Izclose(&finptr); + } + if (1 < RCSstat.st_nlink) + rcswarn("breaking hard link"); + aflush(frewrite); + seteid(); + ignoreints(); + r = chnamemod( + &frewrite, newRCSname, RCSname, changed, + RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), + newRCStime + ); + e = errno; + keepdirtemp(newRCSname); +# if bad_creat0 + lr = un_link(lockname); + le = errno; + keepdirtemp(lockname); +# endif + restoreints(); + setrid(); + if (r != 0) { + enerror(e, RCSname); + error("saved in %s", newRCSname); + } +# if bad_creat0 + if (lr != 0) { + enerror(le, lockname); + r = -1; + } +# endif + } + return r; +} + + void +ORCSclose() +{ + if (0 <= fdlock) { + if (close(fdlock) != 0) + efaterror(lockname); + fdlock = -1; + } + Ozclose(&frewrite); +} + + void +ORCSerror() +/* +* Like ORCSclose, except we are cleaning up after an interrupt or fatal error. +* Do not report errors, since this may loop. This is needed only because +* some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and +* some nearly-Posix hosts (e.g. NFS) work better if the files are closed first. +* This isn't a completely reliable away to work around brain-damaged hosts, +* because of the gap between actual file opening and setting frewrite etc., +* but it's better than nothing. +*/ +{ + if (0 <= fdlock) + VOID close(fdlock); + if (frewrite) + /* Avoid fclose, since stdio may not be reentrant. */ + VOID close(fileno(frewrite)); +} diff --git a/gnu/usr.bin/rcs/lib/rcsfcmp.c b/gnu/usr.bin/rcs/lib/rcsfcmp.c new file mode 100644 index 00000000000..ef0529001b8 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsfcmp.c @@ -0,0 +1,354 @@ +/* Compare working files, ignoring RCS keyword strings. */ + +/***************************************************************************** + * rcsfcmp() + * Testprogram: define FCMPTEST + ***************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + + +/* + * Revision 5.14 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.13 1995/06/01 16:23:43 eggert + * (rcsfcmp): Add -kb support. + * + * Revision 5.12 1994/03/17 14:05:48 eggert + * Normally calculate the $Log prefix from context, not from RCS file. + * Calculate line numbers correctly even if the $Log prefix contains newlines. + * Remove lint. + * + * Revision 5.11 1993/11/03 17:42:27 eggert + * Fix yet another off-by-one error when comparing Log string expansions. + * + * Revision 5.10 1992/07/28 16:12:44 eggert + * Statement macro names now end in _. + * + * Revision 5.9 1991/10/07 17:32:46 eggert + * Count log lines correctly. + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Tune. + * + * Revision 5.7 1991/04/21 11:58:22 eggert + * Fix errno bug. Add MS-DOS support. + * + * Revision 5.6 1991/02/28 19:18:47 eggert + * Open work file at most once. + * + * Revision 5.5 1990/11/27 09:26:05 eggert + * Fix comment leader bug. + * + * Revision 5.4 1990/11/01 05:03:42 eggert + * Permit arbitrary data in logs and comment leaders. + * + * Revision 5.3 1990/09/11 02:41:15 eggert + * Don't ignore differences inside keyword strings if -ko is set. + * + * Revision 5.1 1990/08/29 07:13:58 eggert + * Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:49 eggert + * Don't append "checked in with -k by " log to logs, + * so that checking in a program with -k doesn't change it. + * Ansify and Posixate. Remove lint. + * + * Revision 4.5 89/05/01 15:12:42 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 88/08/09 19:12:50 eggert + * Shrink stdio code size. + * + * Revision 4.3 87/12/18 11:40:02 narten + * lint cleanups (Guy Harris) + * + * Revision 4.2 87/10/18 10:33:06 narten + * updting version number. Changes relative to 1.1 actually relative to + * 4.1 + * + * Revision 1.2 87/03/27 14:22:19 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 16:24:04 wft + * Marker matching now uses trymatch(). Marker pattern is now + * checked precisely. + * + * Revision 3.1 82/12/04 13:21:40 wft + * Initial revision. + * + */ + +/* +#define FCMPTEST +*/ +/* Testprogram; prints out whether two files are identical, + * except for keywords + */ + +#include "rcsbase.h" + +libId(fcmpId, "$FreeBSD$") + + static int discardkeyval P((int,RILE*)); + static int +discardkeyval(c, f) + register int c; + register RILE *f; +{ + for (;;) + switch (c) { + case KDELIM: + case '\n': + return c; + default: + Igeteof_(f, c, return EOF;) + break; + } +} + + int +rcsfcmp(xfp, xstatp, uname, delta) + register RILE *xfp; + struct stat const *xstatp; + char const *uname; + struct hshentry const *delta; +/* Compare the files xfp and uname. Return zero + * if xfp has the same contents as uname and neither has keywords, + * otherwise -1 if they are the same ignoring keyword values, + * and 1 if they differ even ignoring + * keyword values. For the LOG-keyword, rcsfcmp skips the log message + * given by the parameter delta in xfp. Thus, rcsfcmp returns nonpositive + * if xfp contains the same as uname, with the keywords expanded. + * Implementation: character-by-character comparison until $ is found. + * If a $ is found, read in the marker keywords; if they are real keywords + * and identical, read in keyword value. If value is terminated properly, + * disregard it and optionally skip log message; otherwise, compare value. + */ +{ + register int xc, uc; + char xkeyword[keylength+2]; + int eqkeyvals; + register RILE *ufp; + register int xeof, ueof; + register char * tp; + register char const *sp; + register size_t leaderlen; + int result; + enum markers match1; + struct stat ustat; + + if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) { + efaterror(uname); + } + xeof = ueof = false; + if (MIN_UNEXPAND <= Expand) { + if (!(result = xstatp->st_size!=ustat.st_size)) { +# if large_memory && maps_memory + result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size); +# else + for (;;) { + /* get the next characters */ + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) + if (xeof | ueof) + goto eof; + if (xc != uc) + goto return1; + } +# endif + } + } else { + xc = 0; + uc = 0; /* Keep lint happy. */ + leaderlen = 0; + result = 0; + + for (;;) { + if (xc != KDELIM) { + /* get the next characters */ + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) + if (xeof | ueof) + goto eof; + } else { + /* try to get both keywords */ + tp = xkeyword; + for (;;) { + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) + if (xeof | ueof) + goto eof; + if (xc != uc) + break; + switch (xc) { + default: + if (xkeyword+keylength <= tp) + break; + *tp++ = xc; + continue; + case '\n': case KDELIM: case VDELIM: + break; + } + break; + } + if ( + (xc==KDELIM || xc==VDELIM) && (uc==KDELIM || uc==VDELIM) && + (*tp = xc, (match1 = trymatch(xkeyword)) != Nomatch) + ) { +#ifdef FCMPTEST + VOID printf("found common keyword %s\n",xkeyword); +#endif + result = -1; + for (;;) { + if (xc != uc) { + xc = discardkeyval(xc, xfp); + uc = discardkeyval(uc, ufp); + if ((xeof = xc==EOF) | (ueof = uc==EOF)) + goto eof; + eqkeyvals = false; + break; + } + switch (xc) { + default: + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) + if (xeof | ueof) + goto eof; + continue; + + case '\n': case KDELIM: + eqkeyvals = true; + break; + } + break; + } + if (xc != uc) + goto return1; + if (xc==KDELIM) { + /* Skip closing KDELIM. */ + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) + if (xeof | ueof) + goto eof; + /* if the keyword is LOG, also skip the log message in xfp*/ + if (match1==Log) { + /* first, compute the number of line feeds in log msg */ + int lncnt; + size_t ls, ccnt; + sp = delta->log.string; + ls = delta->log.size; + if (ls31 chars) name. + * while (E) ; -> while (E) continue; + * + * Revision 5.8 1991/09/24 00:28:40 eggert + * Don't export bindex(). + * + * Revision 5.7 1991/08/19 03:13:55 eggert + * Fix messages when rcswriteopen fails. + * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune. + * + * Revision 5.6 1991/04/21 11:58:23 eggert + * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.5 1991/02/26 17:48:38 eggert + * Fix setuid bug. Support new link behavior. + * Define more portable getcwd(). + * + * Revision 5.4 1990/11/01 05:03:43 eggert + * Permit arbitrary data in comment leaders. + * + * Revision 5.3 1990/09/14 22:56:16 hammer + * added more filename extensions and their comment leaders + * + * Revision 5.2 1990/09/04 08:02:23 eggert + * Fix typo when !RCSSEP. + * + * Revision 5.1 1990/08/29 07:13:59 eggert + * Work around buggy compilers with defective argument promotion. + * + * Revision 5.0 1990/08/22 08:12:50 eggert + * Ignore signals when manipulating the semaphore file. + * Modernize list of filename extensions. + * Permit paths of arbitrary length. Beware filenames beginning with "-". + * Remove compile-time limits; use malloc instead. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. + * Don't use access(). Fix test for non-regular files. Tune. + * + * Revision 4.8 89/05/01 15:09:41 narten + * changed getwd to not stat empty directories. + * + * Revision 4.7 88/08/09 19:12:53 eggert + * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint. + * + * Revision 4.6 87/12/18 11:40:23 narten + * additional file types added from 4.3 BSD version, and SPARC assembler + * comment character added. Also, more lint cleanups. (Guy Harris) + * + * Revision 4.5 87/10/18 10:34:16 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to verion 4.3 + * + * Revision 1.3 87/03/27 14:22:21 jenkins + * Port to suns + * + * Revision 1.2 85/06/26 07:34:28 svb + * Comment leader '% ' for '*.tex' files added. + * + * Revision 4.3 83/12/15 12:26:48 wft + * Added check for KDELIM in filenames to pairfilenames(). + * + * Revision 4.2 83/12/02 22:47:45 wft + * Added csh, red, and sl filename suffixes. + * + * Revision 4.1 83/05/11 16:23:39 wft + * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames(): + * 1. added copying of path from workfile to RCS file, if RCS file is omitted; + * 2. added getting the file status of RCS and working files; + * 3. added ignoring of directories. + * + * Revision 3.7 83/05/11 15:01:58 wft + * Added comtable[] which pairs filename suffixes with comment leaders; + * updated InitAdmin() accordingly. + * + * Revision 3.6 83/04/05 14:47:36 wft + * fixed Suffix in InitAdmin(). + * + * Revision 3.5 83/01/17 18:01:04 wft + * Added getwd() and rename(); these can be removed by defining + * V4_2BSD, since they are not needed in 4.2 bsd. + * Changed sys/param.h to sys/types.h. + * + * Revision 3.4 82/12/08 21:55:20 wft + * removed unused variable. + * + * Revision 3.3 82/11/28 20:31:37 wft + * Changed mktempfile() to store the generated filenames. + * Changed getfullRCSname() to store the file and pathname, and to + * delete leading "../" and "./". + * + * Revision 3.2 82/11/12 14:29:40 wft + * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(), + * checksuffix(), checkfullpath(). Semaphore name generation updated. + * mktempfile() now checks for nil path; freefilename initialized properly. + * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST. + * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here. + * + * Revision 3.1 82/10/18 14:51:28 wft + * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h). + * renamed checkpath() to checkfullpath(). + */ + + +#include "rcsbase.h" + +libId(fnmsId, "$FreeBSD$") + +static char const *bindex P((char const*,int)); +static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int)); +static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int)); +static int suffix_matches P((char const*,char const*)); +static size_t dir_useful_len P((char const*)); +static size_t suffixlen P((char const*)); +static void InitAdmin P((void)); + +char const *RCSname; +char *workname; +int fdlock; +FILE *workstdout; +struct stat RCSstat; +char const *suffixes; + +static char const rcsdir[] = "RCS"; +#define rcslen (sizeof(rcsdir)-1) + +static struct buf RCSbuf, RCSb; +static int RCSerrno; + + +/* Temp names to be unlinked when done, if they are not 0. */ +#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */ +static char *volatile tpnames[TEMPNAMES]; + + +struct compair { + char const *suffix, *comlead; +}; + +/* +* This table is present only for backwards compatibility. +* Normally we ignore this table, and use the prefix of the `$Log' line instead. +*/ +static struct compair const comtable[] = { + { "a" , "-- " }, /* Ada */ + { "ada" , "-- " }, + { "adb" , "-- " }, + { "ads" , "-- " }, + { "asm" , ";; " }, /* assembler (MS-DOS) */ + { "bat" , ":: " }, /* batch (MS-DOS) */ + { "body", "-- " }, /* Ada */ + { "c" , " * " }, /* C */ + { "c++" , "// " }, /* C++ in all its infinite guises */ + { "cc" , "// " }, + { "cpp" , "// " }, + { "cxx" , "// " }, + { "cl" , ";;; "}, /* Common Lisp */ + { "cmd" , ":: " }, /* command (OS/2) */ + { "cmf" , "c " }, /* CM Fortran */ + { "cs" , " * " }, /* C* */ + { "el" , "; " }, /* Emacs Lisp */ + { "f" , "c " }, /* Fortran */ + { "for" , "c " }, + { "h" , " * " }, /* C-header */ + { "hpp" , "// " }, /* C++ header */ + { "hxx" , "// " }, + { "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */ + { "lisp", ";;; "}, /* Lucid Lisp */ + { "lsp" , ";; " }, /* Microsoft Lisp */ + { "m" , "// " }, /* Objective C */ + { "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */ + { "me" , ".\\\" "}, /* troff -me */ + { "ml" , "; " }, /* mocklisp */ + { "mm" , ".\\\" "}, /* troff -mm */ + { "ms" , ".\\\" "}, /* troff -ms */ + { "p" , " * " }, /* Pascal */ + { "pas" , " * " }, + { "ps" , "% " }, /* PostScript */ + { "spec", "-- " }, /* Ada */ + { "sty" , "% " }, /* LaTeX style */ + { "tex" , "% " }, /* TeX */ + { "y" , " * " }, /* yacc */ + { 0 , "# " } /* default for unknown suffix; must be last */ +}; + +#if has_mktemp + static char const *tmp P((void)); + static char const * +tmp() +/* Yield the name of the tmp directory. */ +{ + static char const *s; + if (!s + && !(s = cgetenv("TMPDIR")) /* Unix tradition */ + && !(s = cgetenv("TMP")) /* DOS tradition */ + && !(s = cgetenv("TEMP")) /* another DOS tradition */ + ) + s = TMPDIR; + return s; +} +#endif + + char const * +maketemp(n) + int n; +/* Create a unique pathname using n and the process id and store it + * into the nth slot in tpnames. + * Because of storage in tpnames, tempunlink() can unlink the file later. + * Return a pointer to the pathname created. + */ +{ + char *p; + char const *t = tpnames[n]; +# if has_mktemp + int fd; +# endif + + if (t) + return t; + + catchints(); + { +# if has_mktemp + char const *tp = tmp(); + size_t tplen = dir_useful_len(tp); + p = testalloc(tplen + 10); + VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n); + fd = mkstemp(p); + if (fd < 0 || !*p) + faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'", + (int)tplen, tp, SLASH, '0'+n + ); + close(fd); +# else + static char tpnamebuf[TEMPNAMES][L_tmpnam]; + p = tpnamebuf[n]; + if (!tmpnam(p) || !*p) +# ifdef P_tmpdir + faterror("can't make temporary pathname `%s...'",P_tmpdir); +# else + faterror("can't make temporary pathname"); +# endif +# endif + } + + tpnames[n] = p; + return p; +} + + void +tempunlink() +/* Clean up maketemp() files. May be invoked by signal handler. + */ +{ + register int i; + register char *p; + + for (i = TEMPNAMES; 0 <= --i; ) + if ((p = tpnames[i])) { + VOID unlink(p); + /* + * We would tfree(p) here, + * but this might dump core if we're handing a signal. + * We're about to exit anyway, so we won't bother. + */ + tpnames[i] = 0; + } +} + + + static char const * +bindex(sp, c) + register char const *sp; + register int c; +/* Function: Finds the last occurrence of character c in string sp + * and returns a pointer to the character just beyond it. If the + * character doesn't occur in the string, sp is returned. + */ +{ + register char const *r; + r = sp; + while (*sp) { + if (*sp++ == c) r=sp; + } + return r; +} + + + + static int +suffix_matches(suffix, pattern) + register char const *suffix, *pattern; +{ + register int c; + if (!pattern) + return true; + for (;;) + switch (*suffix++ - (c = *pattern++)) { + case 0: + if (!c) + return true; + break; + + case 'A'-'a': + if (ctab[c] == Letter) + break; + /* fall into */ + default: + return false; + } +} + + + static void +InitAdmin() +/* function: initializes an admin node */ +{ + register char const *Suffix; + register int i; + + Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0; + StrictLocks=STRICT_LOCKING; + + /* guess the comment leader from the suffix*/ + Suffix = bindex(workname, '.'); + if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/ + for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++) + continue; + Comment.string = comtable[i].comlead; + Comment.size = strlen(comtable[i].comlead); + Expand = KEYVAL_EXPAND; + clear_buf(&Ignored); + Lexinit(); /* note: if !finptr, reads nothing; only initializes */ +} + + + + void +bufalloc(b, size) + register struct buf *b; + size_t size; +/* Ensure *B is a name buffer of at least SIZE bytes. + * *B's old contents can be freed; *B's new contents are undefined. + */ +{ + if (b->size < size) { + if (b->size) + tfree(b->string); + else + b->size = sizeof(malloc_type); + while (b->size < size) + b->size <<= 1; + b->string = tnalloc(char, b->size); + } +} + + void +bufrealloc(b, size) + register struct buf *b; + size_t size; +/* like bufalloc, except *B's old contents, if any, are preserved */ +{ + if (b->size < size) { + if (!b->size) + bufalloc(b, size); + else { + while ((b->size <<= 1) < size) + continue; + b->string = trealloc(char, b->string, b->size); + } + } +} + + void +bufautoend(b) + struct buf *b; +/* Free an auto buffer at block exit. */ +{ + if (b->size) + tfree(b->string); +} + + struct cbuf +bufremember(b, s) + struct buf *b; + size_t s; +/* + * Free the buffer B with used size S. + * Yield a cbuf with identical contents. + * The cbuf will be reclaimed when this input file is finished. + */ +{ + struct cbuf cb; + + if ((cb.size = s)) + cb.string = fremember(trealloc(char, b->string, s)); + else { + bufautoend(b); /* not really auto */ + cb.string = ""; + } + return cb; +} + + char * +bufenlarge(b, alim) + register struct buf *b; + char const **alim; +/* Make *B larger. Set *ALIM to its new limit, and yield the relocated value + * of its old limit. + */ +{ + size_t s = b->size; + bufrealloc(b, s + 1); + *alim = b->string + b->size; + return b->string + s; +} + + void +bufscat(b, s) + struct buf *b; + char const *s; +/* Concatenate S to B's end. */ +{ + size_t blen = b->string ? strlen(b->string) : 0; + bufrealloc(b, blen+strlen(s)+1); + VOID strcpy(b->string+blen, s); +} + + void +bufscpy(b, s) + struct buf *b; + char const *s; +/* Copy S into B. */ +{ + bufalloc(b, strlen(s)+1); + VOID strcpy(b->string, s); +} + + + char const * +basefilename(p) + char const *p; +/* Yield the address of the base filename of the pathname P. */ +{ + register char const *b = p, *q = p; + for (;;) + switch (*q++) { + case SLASHes: b = q; break; + case 0: return b; + } +} + + + static size_t +suffixlen(x) + char const *x; +/* Yield the length of X, an RCS pathname suffix. */ +{ + register char const *p; + + p = x; + for (;;) + switch (*p) { + case 0: case SLASHes: + return p - x; + + default: + ++p; + continue; + } +} + + char const * +rcssuffix(name) + char const *name; +/* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */ +{ + char const *x, *p, *nz; + size_t nl, xl; + + nl = strlen(name); + nz = name + nl; + x = suffixes; + do { + if ((xl = suffixlen(x))) { + if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0) + return p; + } else + for (p = name; p < nz - rcslen; p++) + if ( + isSLASH(p[rcslen]) + && (p==name || isSLASH(p[-1])) + && memcmp(p, rcsdir, rcslen) == 0 + ) + return nz; + x += xl; + } while (*x++); + return 0; +} + + /*ARGSUSED*/ RILE * +rcsreadopen(RCSpath, status, mustread) + struct buf *RCSpath; + struct stat *status; + int mustread; +/* Open RCSPATH for reading and yield its FILE* descriptor. + * If successful, set *STATUS to its status. + * Pass this routine to pairnames() for read-only access to the file. */ +{ + return Iopen(RCSpath->string, FOPEN_RB, status); +} + + static int +finopen(rcsopen, mustread) + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread; +/* + * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. + * Set finptr to the result and yield true if successful. + * RCSb holds the file's name. + * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. + * Yield true if successful or if an unusual failure. + */ +{ + int interesting, preferold; + + /* + * We prefer an old name to that of a nonexisting new RCS file, + * unless we tried locking the old name and failed. + */ + preferold = RCSbuf.string[0] && (mustread||0<=fdlock); + + finptr = (*rcsopen)(&RCSb, &RCSstat, mustread); + interesting = finptr || errno!=ENOENT; + if (interesting || !preferold) { + /* Use the new name. */ + RCSerrno = errno; + bufscpy(&RCSbuf, RCSb.string); + } + return interesting; +} + + static int +fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) + char const *d, *base, *x; + size_t dlen, baselen, xlen; + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread; +/* + * D is a directory name with length DLEN (including trailing slash). + * BASE is a filename with length BASELEN. + * X is an RCS pathname suffix with length XLEN. + * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. + * Yield true if successful. + * Try dRCS/basex first; if that fails and x is nonempty, try dbasex. + * Put these potential names in RCSb. + * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. + * Yield true if successful or if an unusual failure. + */ +{ + register char *p; + + bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1); + + /* Try dRCS/basex. */ + VOID memcpy(p = RCSb.string, d, dlen); + VOID memcpy(p += dlen, rcsdir, rcslen); + p += rcslen; + *p++ = SLASH; + VOID memcpy(p, base, baselen); + VOID memcpy(p += baselen, x, xlen); + p[xlen] = 0; + if (xlen) { + if (finopen(rcsopen, mustread)) + return true; + + /* Try dbasex. */ + /* Start from scratch, because finopen() may have changed RCSb. */ + VOID memcpy(p = RCSb.string, d, dlen); + VOID memcpy(p += dlen, base, baselen); + VOID memcpy(p += baselen, x, xlen); + p[xlen] = 0; + } + return finopen(rcsopen, mustread); +} + + int +pairnames(argc, argv, rcsopen, mustread, quiet) + int argc; + char **argv; + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread, quiet; +/* + * Pair the pathnames pointed to by argv; argc indicates + * how many there are. + * Place a pointer to the RCS pathname into RCSname, + * and a pointer to the pathname of the working file into workname. + * If both are given, and workstdout + * is set, a warning is printed. + * + * If the RCS file exists, places its status into RCSstat. + * + * If the RCS file exists, it is RCSOPENed for reading, the file pointer + * is placed into finptr, and the admin-node is read in; returns 1. + * If the RCS file does not exist and MUSTREAD, + * print an error unless QUIET and return 0. + * Otherwise, initialize the admin node and return -1. + * + * 0 is returned on all errors, e.g. files that are not regular files. + */ +{ + static struct buf tempbuf; + + register char *p, *arg, *RCS1; + char const *base, *RCSbase, *x; + int paired; + size_t arglen, dlen, baselen, xlen; + + fdlock = -1; + + if (!(arg = *argv)) return 0; /* already paired pathname */ + if (*arg == '-') { + error("%s option is ignored after pathnames", arg); + return 0; + } + + base = basefilename(arg); + paired = false; + + /* first check suffix to see whether it is an RCS file or not */ + if ((x = rcssuffix(arg))) + { + /* RCS pathname given */ + RCS1 = arg; + RCSbase = base; + baselen = x - base; + if ( + 1 < argc && + !rcssuffix(workname = p = argv[1]) && + baselen <= (arglen = strlen(p)) && + ((p+=arglen-baselen) == workname || isSLASH(p[-1])) && + memcmp(base, p, baselen) == 0 + ) { + argv[1] = 0; + paired = true; + } else { + bufscpy(&tempbuf, base); + workname = p = tempbuf.string; + p[baselen] = 0; + } + } else { + /* working file given; now try to find RCS file */ + workname = arg; + baselen = strlen(base); + /* Derive RCS pathname. */ + if ( + 1 < argc && + (x = rcssuffix(RCS1 = argv[1])) && + baselen <= x - RCS1 && + ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) && + memcmp(base, RCSbase, baselen) == 0 + ) { + argv[1] = 0; + paired = true; + } else + RCSbase = RCS1 = 0; + } + /* Now we have a (tentative) RCS pathname in RCS1 and workname. */ + /* Second, try to find the right RCS file */ + if (RCSbase!=RCS1) { + /* a path for RCSfile is given; single RCS file to look for */ + bufscpy(&RCSbuf, RCS1); + finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread); + RCSerrno = errno; + } else { + bufscpy(&RCSbuf, ""); + if (RCS1) + /* RCS filename was given without path. */ + VOID fin2open(arg, (size_t)0, RCSbase, baselen, + x, strlen(x), rcsopen, mustread + ); + else { + /* No RCS pathname was given. */ + /* Try each suffix in turn. */ + dlen = base-arg; + x = suffixes; + while (! fin2open(arg, dlen, base, baselen, + x, xlen=suffixlen(x), rcsopen, mustread + )) { + x += xlen; + if (!*x++) + break; + } + } + } + RCSname = p = RCSbuf.string; + if (finptr) { + if (!S_ISREG(RCSstat.st_mode)) { + error("%s isn't a regular file -- ignored", p); + return 0; + } + Lexinit(); getadmin(); + } else { + if (RCSerrno!=ENOENT || mustread || fdlock<0) { + if (RCSerrno == EEXIST) + error("RCS file %s is in use", p); + else if (!quiet || RCSerrno!=ENOENT) + enerror(RCSerrno, p); + return 0; + } + InitAdmin(); + }; + + if (paired && workstdout) + workwarn("Working file ignored due to -p option"); + + prevkeys = false; + return finptr ? 1 : -1; +} + + + char const * +getfullRCSname() +/* + * Return a pointer to the full pathname of the RCS file. + * Remove leading `./'. + */ +{ + if (ROOTPATH(RCSname)) { + return RCSname; + } else { + static struct buf rcsbuf; +# if needs_getabsname + bufalloc(&rcsbuf, SIZEABLE_PATH + 1); + while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0) + if (errno == ERANGE) + bufalloc(&rcsbuf, rcsbuf.size<<1); + else + efaterror("getabsname"); +# else + static char const *wdptr; + static struct buf wdbuf; + static size_t wdlen; + + register char const *r; + register size_t dlen; + register char *d; + register char const *wd; + + if (!(wd = wdptr)) { + /* Get working directory for the first time. */ + char *PWD = cgetenv("PWD"); + struct stat PWDstat, dotstat; + if (! ( + (d = PWD) && + ROOTPATH(PWD) && + stat(PWD, &PWDstat) == 0 && + stat(".", &dotstat) == 0 && + same_file(PWDstat, dotstat, 1) + )) { + bufalloc(&wdbuf, SIZEABLE_PATH + 1); +# if has_getcwd || !has_getwd + while (!(d = getcwd(wdbuf.string, wdbuf.size))) + if (errno == ERANGE) + bufalloc(&wdbuf, wdbuf.size<<1); + else if ((d = PWD)) + break; + else + efaterror("getcwd"); +# else + d = getwd(wdbuf.string); + if (!d && !(d = PWD)) + efaterror("getwd"); +# endif + } + wdlen = dir_useful_len(d); + d[wdlen] = 0; + wdptr = wd = d; + } + /* + * Remove leading `./'s from RCSname. + * Do not try to handle `../', since removing it may yield + * the wrong answer in the presence of symbolic links. + */ + for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2) + /* `.////' is equivalent to `./'. */ + while (isSLASH(r[2])) + r++; + /* Build full pathname. */ + dlen = wdlen; + bufalloc(&rcsbuf, dlen + strlen(r) + 2); + d = rcsbuf.string; + VOID memcpy(d, wd, dlen); + d += dlen; + *d++ = SLASH; + VOID strcpy(d, r); +# endif + return rcsbuf.string; + } +} + +/* Derived from code from the XFree86 project */ + char const * +getfullCVSname() +/* Function: returns a pointer to the path name of the RCS file with the + * CVSROOT part stripped off, and with 'Attic/' stripped off (if present). + */ +{ + +#define ATTICDIR "/Attic" + + char const *namebuf = getfullRCSname(); + char *cvsroot = cgetenv("CVSROOT"); + int cvsrootlen; + char *c = NULL; + int alen = strlen(ATTICDIR); + + if ((c = strrchr(namebuf, '/')) != NULL) { + if (namebuf - c >= alen) { + if (!strncmp(c - alen, ATTICDIR, alen)) { + while(*c != '\0') { + *(c - alen) = *c; + c++; + } + *(c - alen) = '\0'; + } + } + } + + if (!cvsroot) + return(namebuf); + else + { + cvsrootlen = strlen(cvsroot); + if (!strncmp(namebuf, cvsroot, cvsrootlen) && + namebuf[cvsrootlen] == '/') + return(namebuf + cvsrootlen + 1); + else + return(namebuf); + } +} + + static size_t +dir_useful_len(d) + char const *d; +/* +* D names a directory; yield the number of characters of D's useful part. +* To create a file in D, append a SLASH and a file name to D's useful part. +* Ignore trailing slashes if possible; not only are they ugly, +* but some non-Posix systems misbehave unless the slashes are omitted. +*/ +{ +# ifndef SLASHSLASH_is_SLASH +# define SLASHSLASH_is_SLASH 0 +# endif + size_t dlen = strlen(d); + if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1])) + --dlen; + else + while (dlen && isSLASH(d[dlen-1])) + --dlen; + return dlen; +} + +#ifndef isSLASH + int +isSLASH(c) + int c; +{ + switch (c) { + case SLASHes: + return true; + default: + return false; + } +} +#endif + + +#if !has_getcwd && !has_getwd + + char * +getcwd(path, size) + char *path; + size_t size; +{ + static char const usrbinpwd[] = "/usr/bin/pwd"; +# define binpwd (usrbinpwd+4) + + register FILE *fp; + register int c; + register char *p, *lim; + int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus; + pid_t child; + + if (!size) { + errno = EINVAL; + return 0; + } + if (pipe(fd) != 0) + return 0; +# if bad_wait_if_SIGCHLD_ignored +# ifndef SIGCHLD +# define SIGCHLD SIGCLD +# endif + VOID signal(SIGCHLD, SIG_DFL); +# endif + if (!(child = vfork())) { + if ( + close(fd[0]) == 0 && + (fd[1] == STDOUT_FILENO || +# ifdef F_DUPFD + (VOID close(STDOUT_FILENO), + fcntl(fd[1], F_DUPFD, STDOUT_FILENO)) +# else + dup2(fd[1], STDOUT_FILENO) +# endif + == STDOUT_FILENO && + close(fd[1]) == 0 + ) + ) { + VOID close(STDERR_FILENO); + VOID execl(binpwd, binpwd, (char *)0); + VOID execl(usrbinpwd, usrbinpwd, (char *)0); + } + _exit(EXIT_FAILURE); + } + e = errno; + closeerror = close(fd[1]); + closeerrno = errno; + fp = 0; + readerror = toolong = wstatus = 0; + p = path; + if (0 <= child) { + fp = fdopen(fd[0], "r"); + e = errno; + if (fp) { + lim = p + size; + for (p = path; ; *p++ = c) { + if ((c=getc(fp)) < 0) { + if (feof(fp)) + break; + if (ferror(fp)) { + readerror = 1; + e = errno; + break; + } + } + if (p == lim) { + toolong = 1; + break; + } + } + } +# if has_waitpid + if (waitpid(child, &wstatus, 0) < 0) + wstatus = 1; +# else + { + pid_t w; + do { + if ((w = wait(&wstatus)) < 0) { + wstatus = 1; + break; + } + } while (w != child); + } +# endif + } + if (!fp) { + VOID close(fd[0]); + errno = e; + return 0; + } + if (fclose(fp) != 0) + return 0; + if (readerror) { + errno = e; + return 0; + } + if (closeerror) { + errno = closeerrno; + return 0; + } + if (toolong) { + errno = ERANGE; + return 0; + } + if (wstatus || p == path || *--p != '\n') { + errno = EACCES; + return 0; + } + *p = '\0'; + return path; +} +#endif + + +#ifdef PAIRTEST +/* test program for pairnames() and getfullRCSname() */ + +char const cmdid[] = "pair"; + +main(argc, argv) +int argc; char *argv[]; +{ + int result; + int initflag; + quietflag = initflag = false; + + while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + + case 'p': workstdout = stdout; + break; + case 'i': initflag=true; + break; + case 'q': quietflag=true; + break; + default: error("unknown option: %s", *argv); + break; + } + } + + do { + RCSname = workname = 0; + result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag); + if (result!=0) { + diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n", + RCSname, workname, getfullRCSname() + ); + } + switch (result) { + case 0: continue; /* already paired file */ + + case 1: if (initflag) { + rcserror("already exists"); + } else { + diagnose("RCS file %s exists\n", RCSname); + } + Ifclose(finptr); + break; + + case -1:diagnose("RCS file doesn't exist\n"); + break; + } + + } while (++argv, --argc>=1); + +} + + void +exiterr() +{ + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} +#endif diff --git a/gnu/usr.bin/rcs/lib/rcsgen.c b/gnu/usr.bin/rcs/lib/rcsgen.c new file mode 100644 index 00000000000..35d8702a34f --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsgen.c @@ -0,0 +1,681 @@ +/* Generate RCS revisions. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.16 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.15 1995/06/01 16:23:43 eggert + * (putadmin): Open RCS file with FOPEN_WB. + * + * Revision 5.14 1994/03/17 14:05:48 eggert + * Work around SVR4 stdio performance bug. + * Flush stderr after prompt. Remove lint. + * + * Revision 5.13 1993/11/03 17:42:27 eggert + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.12 1992/07/28 16:12:44 eggert + * Statement macro names now end in _. + * Be consistent about pathnames vs filenames. + * + * Revision 5.11 1992/01/24 18:44:19 eggert + * Move put routines here from rcssyn.c. + * Add support for bad_creat0. + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Fix log bugs, e.g. ci -t/dev/null when has_mmap. + * + * Revision 5.9 1991/09/10 22:15:46 eggert + * Fix test for redirected stdin. + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Add piece tables. Tune. + * + * Revision 5.7 1991/04/21 11:58:24 eggert + * Add MS-DOS support. + * + * Revision 5.6 1990/12/27 19:54:26 eggert + * Fix bug: rcs -t inserted \n, making RCS file grow. + * + * Revision 5.5 1990/12/04 05:18:45 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:47 eggert + * Add -I and new -t behavior. Permit arbitrary data in logs. + * + * Revision 5.3 1990/09/21 06:12:43 hammer + * made putdesc() treat stdin the same whether or not it was from a terminal + * by making it recognize that a single '.' was then end of the + * description always + * + * Revision 5.2 1990/09/04 08:02:25 eggert + * Fix `co -p1.1 -ko' bug. Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:14:01 eggert + * Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:52 eggert + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. + * + * Revision 4.7 89/05/01 15:12:49 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.6 88/08/28 14:59:10 eggert + * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin() + * + * Revision 4.5 87/12/18 11:43:25 narten + * additional lint cleanups, and a bug fix from the 4.3BSD version that + * keeps "ci" from sticking a '\377' into the description if you run it + * with a zero-length file as the description. (Guy Harris) + * + * Revision 4.4 87/10/18 10:35:10 narten + * Updating version numbers. Changes relative to 1.1 actually relative to + * 4.2 + * + * Revision 1.3 87/09/24 13:59:51 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:27 jenkins + * Port to suns + * + * Revision 4.2 83/12/02 23:01:39 wft + * merged 4.1 and 3.3.1.1 (clearerr(stdin)). + * + * Revision 4.1 83/05/10 16:03:33 wft + * Changed putamin() to abort if trying to reread redirected stdin. + * Fixed getdesc() to output a prompt on initial newline. + * + * Revision 3.3.1.1 83/10/19 04:21:51 lepreau + * Added clearerr(stdin) for re-reading description from stdin. + * + * Revision 3.3 82/11/28 21:36:49 wft + * 4.2 prerelease + * + * Revision 3.3 82/11/28 21:36:49 wft + * Replaced ferror() followed by fclose() with ffclose(). + * Putdesc() now suppresses the prompts if stdin + * is not a terminal. A pointer to the current log message is now + * inserted into the corresponding delta, rather than leaving it in a + * global variable. + * + * Revision 3.2 82/10/18 21:11:26 wft + * I added checks for write errors during editing, and improved + * the prompt on putdesc(). + * + * Revision 3.1 82/10/13 15:55:09 wft + * corrected type of variables assigned to by getc (char --> int) + */ + + + + +#include "rcsbase.h" + +libId(genId, "$FreeBSD$") + +int interactiveflag; /* Should we act as if stdin is a tty? */ +struct buf curlogbuf; /* buffer for current log message */ + +enum stringwork { enter, copy, edit, expand, edit_expand }; + +static void putdelta P((struct hshentry const*,FILE*)); +static void scandeltatext P((struct hshentry*,enum stringwork,int)); + + + + + char const * +buildrevision(deltas, target, outfile, expandflag) + struct hshentries const *deltas; + struct hshentry *target; + FILE *outfile; + int expandflag; +/* Function: Generates the revision given by target + * by retrieving all deltas given by parameter deltas and combining them. + * If outfile is set, the revision is output to it, + * otherwise written into a temporary file. + * Temporary files are allocated by maketemp(). + * if expandflag is set, keyword expansion is performed. + * Return 0 if outfile is set, the name of the temporary file otherwise. + * + * Algorithm: Copy initial revision unchanged. Then edit all revisions but + * the last one into it, alternating input and output files (resultname and + * editname). The last revision is then edited in, performing simultaneous + * keyword substitution (this saves one extra pass). + * All this simplifies if only one revision needs to be generated, + * or no keyword expansion is necessary, or if output goes to stdout. + */ +{ + if (deltas->first == target) { + /* only latest revision to generate */ + openfcopy(outfile); + scandeltatext(target, expandflag?expand:copy, true); + if (outfile) + return 0; + else { + Ozclose(&fcopy); + return resultname; + } + } else { + /* several revisions to generate */ + /* Get initial revision without keyword expansion. */ + scandeltatext(deltas->first, enter, false); + while ((deltas=deltas->rest)->rest) { + /* do all deltas except last one */ + scandeltatext(deltas->first, edit, false); + } + if (expandflag || outfile) { + /* first, get to beginning of file*/ + finishedit((struct hshentry*)0, outfile, false); + } + scandeltatext(target, expandflag?edit_expand:edit, true); + finishedit( + expandflag ? target : (struct hshentry*)0, + outfile, true + ); + if (outfile) + return 0; + Ozclose(&fcopy); + return resultname; + } +} + + + + static void +scandeltatext(delta, func, needlog) + struct hshentry *delta; + enum stringwork func; + int needlog; +/* Function: Scans delta text nodes up to and including the one given + * by delta. For the one given by delta, the log message is saved into + * delta->log if needlog is set; func specifies how to handle the text. + * Similarly, if needlog, delta->igtext is set to the ignored phrases. + * Assumes the initial lexeme must be read in first. + * Does not advance nexttok after it is finished. + */ +{ + struct hshentry const *nextdelta; + struct cbuf cb; + + for (;;) { + if (eoflex()) + fatserror("can't find delta for revision %s", delta->num); + nextlex(); + if (!(nextdelta=getnum())) { + fatserror("delta number corrupted"); + } + getkeystring(Klog); + if (needlog && delta==nextdelta) { + cb = savestring(&curlogbuf); + delta->log = cleanlogmsg(curlogbuf.string, cb.size); + nextlex(); + delta->igtext = getphrases(Ktext); + } else {readstring(); + ignorephrases(Ktext); + } + getkeystring(Ktext); + + if (delta==nextdelta) + break; + readstring(); /* skip over it */ + + } + switch (func) { + case enter: enterstring(); break; + case copy: copystring(); break; + case expand: xpandstring(delta); break; + case edit: editstring((struct hshentry *)0); break; + case edit_expand: editstring(delta); break; + } +} + + struct cbuf +cleanlogmsg(m, s) + char *m; + size_t s; +{ + register char *t = m; + register char const *f = t; + struct cbuf r; + while (s) { + --s; + if ((*t++ = *f++) == '\n') + while (m < --t) + if (t[-1]!=' ' && t[-1]!='\t') { + *t++ = '\n'; + break; + } + } + while (m < t && (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n')) + --t; + r.string = m; + r.size = t - m; + return r; +} + + +int ttystdin() +{ + static int initialized; + if (!initialized) { + if (!interactiveflag) + interactiveflag = isatty(STDIN_FILENO); + initialized = true; + } + return interactiveflag; +} + + int +getcstdin() +{ + register FILE *in; + register int c; + + in = stdin; + if (feof(in) && ttystdin()) + clearerr(in); + c = getc(in); + if (c == EOF) { + testIerror(in); + if (feof(in) && ttystdin()) + afputc('\n',stderr); + } + return c; +} + +#if has_prototypes + int +yesorno(int default_answer, char const *question, ...) +#else + /*VARARGS2*/ int + yesorno(default_answer, question, va_alist) + int default_answer; char const *question; va_dcl +#endif +{ + va_list args; + register int c, r; + if (!quietflag && ttystdin()) { + oflush(); + vararg_start(args, question); + fvfprintf(stderr, question, args); + va_end(args); + eflush(); + r = c = getcstdin(); + while (c!='\n' && !feof(stdin)) + c = getcstdin(); + if (r=='y' || r=='Y') + return true; + if (r=='n' || r=='N') + return false; + } + return default_answer; +} + + + void +putdesc(textflag, textfile) + int textflag; + char *textfile; +/* Function: puts the descriptive text into file frewrite. + * if finptr && !textflag, the text is copied from the old description. + * Otherwise, if textfile, the text is read from that + * file, or from stdin, if !textfile. + * A textfile with a leading '-' is treated as a string, not a pathname. + * If finptr, the old descriptive text is discarded. + * Always clears foutptr. + */ +{ + static struct buf desc; + static struct cbuf desclean; + + register FILE *txt; + register int c; + register FILE * frew; + register char *p; + register size_t s; + char const *plim; + + frew = frewrite; + if (finptr && !textflag) { + /* copy old description */ + aprintf(frew, "\n\n%s%c", Kdesc, nextc); + foutptr = frewrite; + getdesc(false); + foutptr = 0; + } else { + foutptr = 0; + /* get new description */ + if (finptr) { + /*skip old description*/ + getdesc(false); + } + aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM); + if (!textfile) + desclean = getsstdin( + "t-", "description", + "NOTE: This is NOT the log message!\n", &desc + ); + else if (!desclean.string) { + if (*textfile == '-') { + p = textfile + 1; + s = strlen(p); + } else { + if (!(txt = fopenSafer(textfile, "r"))) + efaterror(textfile); + bufalloc(&desc, 1); + p = desc.string; + plim = p + desc.size; + for (;;) { + if ((c=getc(txt)) == EOF) { + testIerror(txt); + if (feof(txt)) + break; + } + if (plim <= p) + p = bufenlarge(&desc, &plim); + *p++ = c; + } + if (fclose(txt) != 0) + Ierror(); + s = p - desc.string; + p = desc.string; + } + desclean = cleanlogmsg(p, s); + } + putstring(frew, false, desclean, true); + aputc_('\n', frew) + } +} + + struct cbuf +getsstdin(option, name, note, buf) + char const *option, *name, *note; + struct buf *buf; +{ + register int c; + register char *p; + register size_t i; + register int tty = ttystdin(); + + if (tty) { + aprintf(stderr, + "enter %s, terminated with single '.' or end of file:\n%s>> ", + name, note + ); + eflush(); + } else if (feof(stdin)) + rcsfaterror("can't reread redirected stdin for %s; use -%s<%s>", + name, option, name + ); + + for ( + i = 0, p = 0; + c = getcstdin(), !feof(stdin); + bufrealloc(buf, i+1), p = buf->string, p[i++] = c + ) + if (c == '\n') + if (i && p[i-1]=='.' && (i==1 || p[i-2]=='\n')) { + /* Remove trailing '.'. */ + --i; + break; + } else if (tty) { + aputs(">> ", stderr); + eflush(); + } + return cleanlogmsg(p, i); +} + + + void +putadmin() +/* Output the admin node. */ +{ + register FILE *fout; + struct assoc const *curassoc; + struct rcslock const *curlock; + struct access const *curaccess; + + if (!(fout = frewrite)) { +# if bad_creat0 + ORCSclose(); + fout = fopenSafer(makedirtemp(0), FOPEN_WB); +# else + int fo = fdlock; + fdlock = -1; + fout = fdopen(fo, FOPEN_WB); +# endif + + if (!(frewrite = fout)) + efaterror(RCSname); + } + + /* + * Output the first character with putc, not printf. + * Otherwise, an SVR4 stdio bug buffers output inefficiently. + */ + aputc_(*Khead, fout) + aprintf(fout, "%s\t%s;\n", Khead + 1, Head?Head->num:""); + if (Dbranch && VERSION(4)<=RCSversion) + aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch); + + aputs(Kaccess, fout); + curaccess = AccessList; + while (curaccess) { + aprintf(fout, "\n\t%s", curaccess->login); + curaccess = curaccess->nextaccess; + } + aprintf(fout, ";\n%s", Ksymbols); + curassoc = Symbols; + while (curassoc) { + aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num); + curassoc = curassoc->nextassoc; + } + aprintf(fout, ";\n%s", Klocks); + curlock = Locks; + while (curlock) { + aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num); + curlock = curlock->nextlock; + } + if (StrictLocks) aprintf(fout, "; %s", Kstrict); + aprintf(fout, ";\n"); + if (Comment.size) { + aprintf(fout, "%s\t", Kcomment); + putstring(fout, true, Comment, false); + aprintf(fout, ";\n"); + } + if (Expand != KEYVAL_EXPAND) + aprintf(fout, "%s\t%c%s%c;\n", + Kexpand, SDELIM, expand_names[Expand], SDELIM + ); + awrite(Ignored.string, Ignored.size, fout); + aputc_('\n', fout) +} + + + static void +putdelta(node, fout) + register struct hshentry const *node; + register FILE * fout; +/* Output the delta NODE to FOUT. */ +{ + struct branchhead const *nextbranch; + + if (!node) return; + + aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches", + node->num, + Kdate, node->date, + Kauthor, node->author, + Kstate, node->state?node->state:"" + ); + nextbranch = node->branches; + while (nextbranch) { + aprintf(fout, "\n\t%s", nextbranch->hsh->num); + nextbranch = nextbranch->nextbranch; + } + + aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:""); + awrite(node->ig.string, node->ig.size, fout); +} + + + void +puttree(root, fout) + struct hshentry const *root; + register FILE *fout; +/* Output the delta tree with base ROOT in preorder to FOUT. */ +{ + struct branchhead const *nextbranch; + + if (!root) return; + + if (root->selector) + putdelta(root, fout); + + puttree(root->next, fout); + + nextbranch = root->branches; + while (nextbranch) { + puttree(nextbranch->hsh, fout); + nextbranch = nextbranch->nextbranch; + } +} + + + int +putdtext(delta, srcname, fout, diffmt) + struct hshentry const *delta; + char const *srcname; + FILE *fout; + int diffmt; +/* + * Output a deltatext node with delta number DELTA->num, log message DELTA->log, + * ignored phrases DELTA->igtext and text SRCNAME to FOUT. + * Double up all SDELIMs in both the log and the text. + * Make sure the log message ends in \n. + * Return false on error. + * If DIFFMT, also check that the text is valid diff -n output. + */ +{ + RILE *fin; + if (!(fin = Iopen(srcname, "r", (struct stat*)0))) { + eerror(srcname); + return false; + } + putdftext(delta, fin, fout, diffmt); + Ifclose(fin); + return true; +} + + void +putstring(out, delim, s, log) + register FILE *out; + struct cbuf s; + int delim, log; +/* + * Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled. + * If LOG is set then S is a log string; append a newline if S is nonempty. + */ +{ + register char const *sp; + register size_t ss; + + if (delim) + aputc_(SDELIM, out) + sp = s.string; + for (ss = s.size; ss; --ss) { + if (*sp == SDELIM) + aputc_(SDELIM, out) + aputc_(*sp++, out) + } + if (s.size && log) + aputc_('\n', out) + aputc_(SDELIM, out) +} + + void +putdftext(delta, finfile, foutfile, diffmt) + struct hshentry const *delta; + RILE *finfile; + FILE *foutfile; + int diffmt; +/* like putdtext(), except the source file is already open */ +{ + declarecache; + register FILE *fout; + register int c; + register RILE *fin; + int ed; + struct diffcmd dc; + + fout = foutfile; + aprintf(fout, DELNUMFORM, delta->num, Klog); + + /* put log */ + putstring(fout, true, delta->log, true); + aputc_('\n', fout) + + /* put ignored phrases */ + awrite(delta->igtext.string, delta->igtext.size, fout); + + /* put text */ + aprintf(fout, "%s\n%c", Ktext, SDELIM); + + fin = finfile; + setupcache(fin); + if (!diffmt) { + /* Copy the file */ + cache(fin); + for (;;) { + cachegeteof_(c, break;) + if (c==SDELIM) aputc_(SDELIM, fout) /*double up SDELIM*/ + aputc_(c, fout) + } + } else { + initdiffcmd(&dc); + while (0 <= (ed = getdiffcmd(fin, false, fout, &dc))) + if (ed) { + cache(fin); + while (dc.nlines--) + do { + cachegeteof_(c, { if (!dc.nlines) goto OK_EOF; unexpected_EOF(); }) + if (c == SDELIM) + aputc_(SDELIM, fout) + aputc_(c, fout) + } while (c != '\n'); + uncache(fin); + } + } + OK_EOF: + aprintf(fout, "%c\n", SDELIM); +} diff --git a/gnu/usr.bin/rcs/lib/rcskeep.c b/gnu/usr.bin/rcs/lib/rcskeep.c new file mode 100644 index 00000000000..4a90f851e61 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcskeep.c @@ -0,0 +1,452 @@ +/* Extract RCS keyword string values from working files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.10 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.9 1995/06/01 16:23:43 eggert + * (getoldkeys): Don't panic if a Name: is empty. + * + * Revision 5.8 1994/03/17 14:05:48 eggert + * Remove lint. + * + * Revision 5.7 1993/11/09 17:40:15 eggert + * Use simpler timezone parsing strategy now that we're using ISO 8601 format. + * + * Revision 5.6 1993/11/03 17:42:27 eggert + * Scan for Name keyword. Improve quality of diagnostics. + * + * Revision 5.5 1992/07/28 16:12:44 eggert + * Statement macro names now end in _. + * + * Revision 5.4 1991/08/19 03:13:55 eggert + * Tune. + * + * Revision 5.3 1991/04/21 11:58:25 eggert + * Shorten names to keep them distinct on shortname hosts. + * + * Revision 5.2 1990/10/04 06:30:20 eggert + * Parse time zone offsets; future RCS versions may output them. + * + * Revision 5.1 1990/09/20 02:38:56 eggert + * ci -k now checks dates more thoroughly. + * + * Revision 5.0 1990/08/22 08:12:53 eggert + * Retrieve old log message if there is one. + * Don't require final newline. + * Remove compile-time limits; use malloc instead. Tune. + * Permit dates past 1999/12/31. Ansify and Posixate. + * + * Revision 4.6 89/05/01 15:12:56 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/09 19:13:03 eggert + * Remove lint and speed up by making FILE *fp local, not global. + * + * Revision 4.4 87/12/18 11:44:21 narten + * more lint cleanups (Guy Harris) + * + * Revision 4.3 87/10/18 10:35:50 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to 4.1 + * + * Revision 1.3 87/09/24 14:00:00 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:29 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 16:26:44 wft + * Added new markers Id and RCSfile; extraction added. + * Marker matching with trymatch(). + * + * Revision 3.2 82/12/24 12:08:26 wft + * added missing #endif. + * + * Revision 3.1 82/12/04 13:22:41 wft + * Initial revision. + * + */ + +#include "rcsbase.h" + +libId(keepId, "$FreeBSD$") + +static int badly_terminated P((void)); +static int checknum P((char const*)); +static int get0val P((int,RILE*,struct buf*,int)); +static int getval P((RILE*,struct buf*,int)); +static int keepdate P((RILE*)); +static int keepid P((int,RILE*,struct buf*)); +static int keeprev P((RILE*)); + +int prevkeys; +struct buf prevauthor, prevdate, prevname, prevrev, prevstate; + + int +getoldkeys(fp) + register RILE *fp; +/* Function: Tries to read keyword values for author, date, + * revision number, and state out of the file fp. + * If fp is null, workname is opened and closed instead of using fp. + * The results are placed into + * prevauthor, prevdate, prevname, prevrev, prevstate. + * Aborts immediately if it finds an error and returns false. + * If it returns true, it doesn't mean that any of the + * values were found; instead, check to see whether the corresponding arrays + * contain the empty string. + */ +{ + register int c; + char keyword[keylength+1]; + register char * tp; + int needs_closing; + int prevname_found; + + if (prevkeys) + return true; + + needs_closing = false; + if (!fp) { + if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) { + eerror(workname); + return false; + } + needs_closing = true; + } + + /* initialize to empty */ + bufscpy(&prevauthor, ""); + bufscpy(&prevdate, ""); + bufscpy(&prevname, ""); prevname_found = 0; + bufscpy(&prevrev, ""); + bufscpy(&prevstate, ""); + + c = '\0'; /* anything but KDELIM */ + for (;;) { + if ( c==KDELIM) { + do { + /* try to get keyword */ + tp = keyword; + for (;;) { + Igeteof_(fp, c, goto ok;) + switch (c) { + default: + if (keyword+keylength <= tp) + break; + *tp++ = c; + continue; + + case '\n': case KDELIM: case VDELIM: + break; + } + break; + } + } while (c==KDELIM); + if (c!=VDELIM) continue; + *tp = c; + Igeteof_(fp, c, break;) + switch (c) { + case ' ': case '\t': break; + default: continue; + } + + switch (trymatch(keyword)) { + case Author: + if (!keepid(0, fp, &prevauthor)) + return false; + c = 0; + break; + case Date: + if (!(c = keepdate(fp))) + return false; + break; + case Header: + case Id: + case LocalId: + if (!( + getval(fp, (struct buf*)0, false) && + keeprev(fp) && + (c = keepdate(fp)) && + keepid(c, fp, &prevauthor) && + keepid(0, fp, &prevstate) + )) + return false; + /* Skip either ``who'' (new form) or ``Locker: who'' (old). */ + if (getval(fp, (struct buf*)0, true) && + getval(fp, (struct buf*)0, true)) + c = 0; + else if (nerror) + return false; + else + c = KDELIM; + break; + case Locker: + (void) getval(fp, (struct buf*)0, false); + c = 0; + break; + case Log: + case RCSfile: + case Source: + if (!getval(fp, (struct buf*)0, false)) + return false; + c = 0; + break; + case Name: + if (getval(fp, &prevname, false)) { + if (*prevname.string) + checkssym(prevname.string); + prevname_found = 1; + } + c = 0; + break; + case Revision: + if (!keeprev(fp)) + return false; + c = 0; + break; + case State: + if (!keepid(0, fp, &prevstate)) + return false; + c = 0; + break; + default: + continue; + } + if (!c) + Igeteof_(fp, c, c=0;) + if (c != KDELIM) { + workerror("closing %c missing on keyword", KDELIM); + return false; + } + if (prevname_found && + *prevauthor.string && *prevdate.string && + *prevrev.string && *prevstate.string + ) + break; + } + Igeteof_(fp, c, break;) + } + + ok: + if (needs_closing) + Ifclose(fp); + else + Irewind(fp); + prevkeys = true; + return true; +} + + static int +badly_terminated() +{ + workerror("badly terminated keyword value"); + return false; +} + + static int +getval(fp, target, optional) + register RILE *fp; + struct buf *target; + int optional; +/* Reads a keyword value from FP into TARGET. + * Returns true if one is found, false otherwise. + * Does not modify target if it is 0. + * Do not report an error if OPTIONAL is set and KDELIM is found instead. + */ +{ + int c; + Igeteof_(fp, c, return badly_terminated();) + return get0val(c, fp, target, optional); +} + + static int +get0val(c, fp, target, optional) + register int c; + register RILE *fp; + struct buf *target; + int optional; +/* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly. + * Same as getval, except C is the lookahead character. + */ +{ register char * tp; + char const *tlim; + register int got1; + + if (target) { + bufalloc(target, 1); + tp = target->string; + tlim = tp + target->size; + } else + tlim = tp = 0; + got1 = false; + for (;;) { + switch (c) { + default: + got1 = true; + if (tp) { + *tp++ = c; + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + } + break; + + case ' ': + case '\t': + if (tp) { + *tp = 0; +# ifdef KEEPTEST + VOID printf("getval: %s\n", target); +# endif + } + return got1; + + case KDELIM: + if (!got1 && optional) + return false; + /* fall into */ + case '\n': + case 0: + return badly_terminated(); + } + Igeteof_(fp, c, return badly_terminated();) + } +} + + + static int +keepdate(fp) + RILE *fp; +/* Function: reads a date prevdate; checks format + * Return 0 on error, lookahead character otherwise. + */ +{ + struct buf prevday, prevtime; + register int c; + + c = 0; + bufautobegin(&prevday); + if (getval(fp,&prevday,false)) { + bufautobegin(&prevtime); + if (getval(fp,&prevtime,false)) { + Igeteof_(fp, c, c=0;) + if (c) { + register char const *d = prevday.string, *t = prevtime.string; + bufalloc(&prevdate, strlen(d) + strlen(t) + 9); + VOID sprintf(prevdate.string, "%s%s %s%s", + /* Parse dates put out by old versions of RCS. */ + isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2]) + ? "19" : "", + d, t, + strchr(t,'-') || strchr(t,'+') ? "" : "+0000" + ); + } + } + bufautoend(&prevtime); + } + bufautoend(&prevday); + return c; +} + + static int +keepid(c, fp, b) + int c; + RILE *fp; + struct buf *b; +/* Get previous identifier from C+FP into B. */ +{ + if (!c) + Igeteof_(fp, c, return false;) + if (!get0val(c, fp, b, false)) + return false; + checksid(b->string); + return !nerror; +} + + static int +keeprev(fp) + RILE *fp; +/* Get previous revision from FP into prevrev. */ +{ + return getval(fp,&prevrev,false) && checknum(prevrev.string); +} + + + static int +checknum(s) + char const *s; +{ + register char const *sp; + register int dotcount = 0; + for (sp=s; ; sp++) { + switch (*sp) { + case 0: + if (dotcount & 1) + return true; + else + break; + + case '.': + dotcount++; + continue; + + default: + if (isdigit(*sp)) + continue; + break; + } + break; + } + workerror("%s is not a revision number", s); + return false; +} + + + +#ifdef KEEPTEST + +/* Print the keyword values found. */ + +char const cmdid[] ="keeptest"; + + int +main(argc, argv) +int argc; char *argv[]; +{ + while (*(++argv)) { + workname = *argv; + getoldkeys((RILE*)0); + VOID printf("%s: revision: %s, date: %s, author: %s, name: %s, state: %s\n", + *argv, prevrev.string, prevdate.string, prevauthor.string, prevname.string, prevstate.string); + } + exitmain(EXIT_SUCCESS); +} +#endif diff --git a/gnu/usr.bin/rcs/lib/rcskeys.c b/gnu/usr.bin/rcs/lib/rcskeys.c new file mode 100644 index 00000000000..378f57dd0f7 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcskeys.c @@ -0,0 +1,186 @@ +/* RCS keyword table and match operation */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.4 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.3 1993/11/03 17:42:27 eggert + * Add Name keyword. + * + * Revision 5.2 1991/08/19 03:13:55 eggert + * Say `T const' instead of `const T'; it's less confusing for pointer types. + * (This change was made in other source files too.) + * + * Revision 5.1 1991/04/21 11:58:25 eggert + * Don't put , just before } in initializer. + * + * Revision 5.0 1990/08/22 08:12:54 eggert + * Add -k. Ansify and Posixate. + * + * Revision 4.3 89/05/01 15:13:02 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.2 87/10/18 10:36:33 narten + * Updating version numbers. Changes relative to 1.1 actuallyt + * relative to 4.1 + * + * Revision 1.2 87/09/24 14:00:10 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 4.1 83/05/04 10:06:53 wft + * Initial revision. + * + */ + + +#include "rcsbase.h" + +libId(keysId, "$FreeBSD$") + + +char const *Keyword[] = { + /* This must be in the same order as rcsbase.h's enum markers type. */ + 0, + AUTHOR, DATE, HEADER, IDH, + LOCKER, LOG, NAME, RCSFILE, REVISION, SOURCE, STATE, CVSHEADER, + NULL +}; + +/* Expand all keywords by default */ +static int ExpandKeyword[] = { + false, + true, true, true, true, + true, true, true, true, true, true, true, true, + true +}; +enum markers LocalIdMode = Id; + + enum markers +trymatch(string) + char const *string; +/* function: Checks whether string starts with a keyword followed + * by a KDELIM or a VDELIM. + * If successful, returns the appropriate marker, otherwise Nomatch. + */ +{ + register int j; + register char const *p, *s; + for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) { + if (!ExpandKeyword[j]) + continue; + /* try next keyword */ + p = Keyword[j]; + if (p == NULL) + continue; + s = string; + while (*p++ == *s++) { + if (!*p) + switch (*s) { + case KDELIM: + case VDELIM: + return (enum markers)j; + default: + return Nomatch; + } + } + } + return(Nomatch); +} + + void +setIncExc(arg) + char const *arg; +/* Sets up the ExpandKeyword table according to command-line flags */ +{ + char *key; + char *copy, *next; + int include = 0, j; + + copy = strdup(arg); + next = copy; + switch (*next++) { + case 'e': + include = false; + break; + case 'i': + include = true; + break; + default: + free(copy); + return; + } + if (include) + for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) + ExpandKeyword[j] = false; + key = strtok(next, ","); + while (key) { + for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) { + if (Keyword[j] == NULL) + continue; + if (!strcmp(key, Keyword[j])) + ExpandKeyword[j] = include; + } + key = strtok(NULL, ","); + } + free(copy); + return; +} + + void +setRCSLocalId(string) + char const *string; +/* function: sets local RCS id and RCSLOCALID envariable */ +{ + static char local_id[keylength+1]; + char *copy, *next, *key; + int j; + + copy = strdup(string); + next = copy; + key = strtok(next, "="); + if (strlen(key) > keylength) + faterror("LocalId is too long"); + VOID strcpy(local_id, key); + Keyword[LocalId] = local_id; + + /* options? */ + while (key = strtok(NULL, ",")) { + if (!strcmp(key, Keyword[Id])) + LocalIdMode=Id; + else if (!strcmp(key, Keyword[Header])) + LocalIdMode=Header; + else if (!strcmp(key, Keyword[CVSHeader])) + LocalIdMode=CVSHeader; + else + error("Unknown LocalId mode"); + } + free(copy); +} diff --git a/gnu/usr.bin/rcs/lib/rcslex.c b/gnu/usr.bin/rcs/lib/rcslex.c new file mode 100644 index 00000000000..7a11f79b46c --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcslex.c @@ -0,0 +1,1568 @@ +/* lexical analysis of RCS files */ + +/****************************************************************************** + * Lexical Analysis. + * hashtable, Lexinit, nextlex, getlex, getkey, + * getid, getnum, readstring, printstring, savestring, + * checkid, fatserror, error, faterror, warn, diagnose + * Testprogram: define LEXDB + ****************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate): + * New functions. + * (Iclose): If large_memory and maps_memory, use them to deallocate mapping. + * (fd2RILE): Use map_fd if available. + * If one mapping method fails, try the next instead of giving up; + * if they all fail, fall back on ordinary read. + * Work around bug: root mmap over NFS succeeds, but accessing dumps core. + * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t. + * (advise_access): Use madvise only if this instance used mmap. + * (Iopen): Use fdSafer to get safer file descriptor. + * (aflush): Moved here from rcsedit.c. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Don't worry if madvise fails. Add Orewind. Remove lint. + * + * Revision 5.16 1993/11/09 17:55:29 eggert + * Fix `label: }' typo. + * + * Revision 5.15 1993/11/03 17:42:27 eggert + * Improve quality of diagnostics by putting file names in them more often. + * Don't discard ignored phrases. + * + * Revision 5.14 1992/07/28 16:12:44 eggert + * Identifiers may now start with a digit and (unless they are symbolic names) + * may contain `.'. Avoid `unsigned'. Statement macro names now end in _. + * + * Revision 5.13 1992/02/17 23:02:27 eggert + * Work around NFS mmap SIGBUS problem. + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * Use OPEN_O_BINARY if mode contains 'b'. + * + * Revision 5.11 1991/11/03 03:30:44 eggert + * Fix porting bug to ancient hosts lacking vfprintf. + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Support piece tables even if !has_mmap. + * + * Revision 5.9 1991/09/24 00:28:42 eggert + * Don't export errsay(). + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Add eoflex(), mmap support. Tune. + * + * Revision 5.7 1991/04/21 11:58:26 eggert + * Add MS-DOS support. + * + * Revision 5.6 1991/02/25 07:12:42 eggert + * Work around fputs bug. strsave -> str_save (DG/UX name clash) + * + * Revision 5.5 1990/12/04 05:18:47 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/19 20:05:28 hammer + * no longer gives warning about unknown keywords if -q is specified + * + * Revision 5.3 1990/11/01 05:03:48 eggert + * When ignoring unknown phrases, copy them to the output RCS file. + * + * Revision 5.2 1990/09/04 08:02:27 eggert + * Count RCS lines better. + * + * Revision 5.1 1990/08/29 07:14:03 eggert + * Work around buggy compilers with defective argument promotion. + * + * Revision 5.0 1990/08/22 08:12:55 eggert + * Remove compile-time limits; use malloc instead. + * Report errno-related errors with perror(). + * Ansify and Posixate. Add support for ISO 8859. + * Use better hash function. + * + * Revision 4.6 89/05/01 15:13:07 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/28 15:01:12 eggert + * Don't loop when writing error messages to a full filesystem. + * Flush stderr/stdout when mixing output. + * Yield exit status compatible with diff(1). + * Shrink stdio code size; allow cc -R; remove lint. + * + * Revision 4.4 87/12/18 11:44:47 narten + * fixed to use "varargs" in "fprintf"; this is required if it is to + * work on a SPARC machine such as a Sun-4 + * + * Revision 4.3 87/10/18 10:37:18 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to version 4.1 + * + * Revision 1.3 87/09/24 14:00:17 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:33 jenkins + * Port to suns + * + * Revision 4.1 83/03/25 18:12:51 wft + * Only changed $Header to $Id. + * + * Revision 3.3 82/12/10 16:22:37 wft + * Improved error messages, changed exit status on error to 1. + * + * Revision 3.2 82/11/28 21:27:10 wft + * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h. + * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations + * properly in case there is an IO-error (e.g., file system full). + * + * Revision 3.1 82/10/11 19:43:56 wft + * removed unused label out:; + * made sure all calls to getc() return into an integer, not a char. + */ + + +/* +#define LEXDB +*/ +/* version LEXDB is for testing the lexical analyzer. The testprogram + * reads a stream of lexemes, enters the revision numbers into the + * hashtable, and prints the recognized tokens. Keywords are recognized + * as identifiers. + */ + + + +#include "rcsbase.h" + +libId(lexId, "$FreeBSD$") + +static char *checkidentifier P((char*,int,int)); +static void errsay P((char const*)); +static void fatsay P((char const*)); +static void lookup P((char const*)); +static void startsay P((const char*,const char*)); +static void warnsay P((char const*)); + +static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ + +enum tokens nexttok; /*next token, set by nextlex */ + +int hshenter; /*if true, next suitable lexeme will be entered */ + /*into the symbol table. Handle with care. */ +int nextc; /*next input character, initialized by Lexinit */ + +long rcsline; /*current line-number of input */ +int nerror; /*counter for errors */ +int quietflag; /*indicates quiet mode */ +RILE * finptr; /*input file descriptor */ + +FILE * frewrite; /*file descriptor for echoing input */ + +FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */ + +static struct buf tokbuf; /* token buffer */ + +char const * NextString; /* next token */ + +/* + * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c, + * so hshsize should be odd. + * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm, + * Software--practice & experience 20, 2 (Feb 1990), 209-224. + */ +#ifndef hshsize +# define hshsize 511 +#endif + +static struct hshentry *hshtab[hshsize]; /*hashtable */ + +static int ignored_phrases; /* have we ignored phrases in this RCS file? */ + + void +warnignore() +{ + if (!ignored_phrases) { + ignored_phrases = true; + rcswarn("Unknown phrases like `%s ...;' are present.", NextString); + } +} + + + + static void +lookup(str) + char const *str; +/* Function: Looks up the character string pointed to by str in the + * hashtable. If the string is not present, a new entry for it is created. + * In any case, the address of the corresponding hashtable entry is placed + * into nexthsh. + */ +{ + register unsigned ihash; /* index into hashtable */ + register char const *sp; + register struct hshentry *n, **p; + + /* calculate hash code */ + sp = str; + ihash = 0; + while (*sp) + ihash = (ihash<<2) + *sp++; + ihash %= hshsize; + + for (p = &hshtab[ihash]; ; p = &n->nexthsh) + if (!(n = *p)) { + /* empty slot found */ + *p = n = ftalloc(struct hshentry); + n->num = fstr_save(str); + n->nexthsh = 0; +# ifdef LEXDB + VOID printf("\nEntered: %s at %u ", str, ihash); +# endif + break; + } else if (strcmp(str, n->num) == 0) + /* match found */ + break; + nexthsh = n; + NextString = n->num; +} + + + + + + + void +Lexinit() +/* Function: Initialization of lexical analyzer: + * initializes the hashtable, + * initializes nextc, nexttok if finptr != 0 + */ +{ register int c; + + for (c = hshsize; 0 <= --c; ) { + hshtab[c] = 0; + } + + nerror = 0; + if (finptr) { + foutptr = 0; + hshenter = true; + ignored_phrases = false; + rcsline = 1; + bufrealloc(&tokbuf, 2); + Iget_(finptr, nextc) + nextlex(); /*initial token*/ + } +} + + + + + + + + void +nextlex() + +/* Function: Reads the next token and sets nexttok to the next token code. + * Only if hshenter is set, a revision number is entered into the + * hashtable and a pointer to it is placed into nexthsh. + * This is useful for avoiding that dates are placed into the hashtable. + * For ID's and NUM's, NextString is set to the character string. + * Assumption: nextc contains the next character. + */ +{ register c; + declarecache; + register FILE *frew; + register char * sp; + char const *limit; + register enum tokens d; + register RILE *fin; + + fin=finptr; frew=foutptr; + setupcache(fin); cache(fin); + c = nextc; + + for (;;) { switch ((d = ctab[c])) { + + default: + fatserror("unknown character `%c'", c); + /*NOTREACHED*/ + + case NEWLN: + ++rcsline; +# ifdef LEXDB + afputc('\n',stdout); +# endif + /* Note: falls into next case */ + + case SPACE: + GETC_(frew, c) + continue; + + case IDCHAR: + case LETTER: + case Letter: + d = ID; + /* fall into */ + case DIGIT: + case PERIOD: + sp = tokbuf.string; + limit = sp + tokbuf.size; + *sp++ = c; + for (;;) { + GETC_(frew, c) + switch (ctab[c]) { + case IDCHAR: + case LETTER: + case Letter: + d = ID; + /* fall into */ + case DIGIT: + case PERIOD: + *sp++ = c; + if (limit <= sp) + sp = bufenlarge(&tokbuf, &limit); + continue; + + default: + break; + } + break; + } + *sp = 0; + if (d == DIGIT || d == PERIOD) { + d = NUM; + if (hshenter) { + lookup(tokbuf.string); + break; + } + } + NextString = fstr_save(tokbuf.string); + break; + + case SBEGIN: /* long string */ + d = STRING; + /* note: only the initial SBEGIN has been read*/ + /* read the string, and reset nextc afterwards*/ + break; + + case COLON: + case SEMI: + GETC_(frew, c) + break; + } break; } + nextc = c; + nexttok = d; + uncache(fin); +} + + int +eoflex() +/* + * Yield true if we look ahead to the end of the input, false otherwise. + * nextc becomes undefined at end of file. + */ +{ + register int c; + declarecache; + register FILE *fout; + register RILE *fin; + + c = nextc; + fin = finptr; + fout = foutptr; + setupcache(fin); cache(fin); + + for (;;) { + switch (ctab[c]) { + default: + nextc = c; + uncache(fin); + return false; + + case NEWLN: + ++rcsline; + /* fall into */ + case SPACE: + cachegeteof_(c, {uncache(fin);return true;}) + break; + } + if (fout) + aputc_(c, fout) + } +} + + +int getlex(token) +enum tokens token; +/* Function: Checks if nexttok is the same as token. If so, + * advances the input by calling nextlex and returns true. + * otherwise returns false. + * Doesn't work for strings and keywords; loses the character string for ids. + */ +{ + if (nexttok==token) { + nextlex(); + return(true); + } else return(false); +} + + int +getkeyopt(key) + char const *key; +/* Function: If the current token is a keyword identical to key, + * advances the input by calling nextlex and returns true; + * otherwise returns false. + */ +{ + if (nexttok==ID && strcmp(key,NextString) == 0) { + /* match found */ + ffree1(NextString); + nextlex(); + return(true); + } + return(false); +} + + void +getkey(key) + char const *key; +/* Check that the current input token is a keyword identical to key, + * and advance the input by calling nextlex. + */ +{ + if (!getkeyopt(key)) + fatserror("missing '%s' keyword", key); +} + + void +getkeystring(key) + char const *key; +/* Check that the current input token is a keyword identical to key, + * and advance the input by calling nextlex; then look ahead for a string. + */ +{ + getkey(key); + if (nexttok != STRING) + fatserror("missing string after '%s' keyword", key); +} + + + char const * +getid() +/* Function: Checks if nexttok is an identifier. If so, + * advances the input by calling nextlex and returns a pointer + * to the identifier; otherwise returns 0. + * Treats keywords as identifiers. + */ +{ + register char const *name; + if (nexttok==ID) { + name = NextString; + nextlex(); + return name; + } else + return 0; +} + + +struct hshentry * getnum() +/* Function: Checks if nexttok is a number. If so, + * advances the input by calling nextlex and returns a pointer + * to the hashtable entry. Otherwise returns 0. + * Doesn't work if hshenter is false. + */ +{ + register struct hshentry * num; + if (nexttok==NUM) { + num=nexthsh; + nextlex(); + return num; + } else + return 0; +} + + struct cbuf +getphrases(key) + char const *key; +/* +* Get a series of phrases that do not start with KEY. Yield resulting buffer. +* Stop when the next phrase starts with a token that is not an identifier, +* or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(), +* this routine assumes nextlex() has already been invoked before we start. +*/ +{ + declarecache; + register int c; + register char const *kn; + struct cbuf r; + register RILE *fin; + register FILE *frew; +# if large_memory +# define savech_(c) ; +# else + register char *p; + char const *limit; + struct buf b; +# define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);} +# endif + + if (nexttok!=ID || strcmp(NextString,key) == 0) + clear_buf(&r); + else { + warnignore(); + fin = finptr; + frew = foutptr; + setupcache(fin); cache(fin); +# if large_memory + r.string = (char const*)cacheptr() - strlen(NextString) - 1; +# else + bufautobegin(&b); + bufscpy(&b, NextString); + p = b.string + strlen(b.string); + limit = b.string + b.size; +# endif + ffree1(NextString); + c = nextc; + for (;;) { + for (;;) { + savech_(c) + switch (ctab[c]) { + default: + fatserror("unknown character `%c'", c); + /*NOTREACHED*/ + case NEWLN: + ++rcsline; + /* fall into */ + case COLON: case DIGIT: case LETTER: case Letter: + case PERIOD: case SPACE: + GETC_(frew, c) + continue; + case SBEGIN: /* long string */ + for (;;) { + for (;;) { + GETC_(frew, c) + savech_(c) + switch (c) { + case '\n': + ++rcsline; + /* fall into */ + default: + continue; + + case SDELIM: + break; + } + break; + } + GETC_(frew, c) + if (c != SDELIM) + break; + savech_(c) + } + continue; + case SEMI: + cacheget_(c) + if (ctab[c] == NEWLN) { + if (frew) + aputc_(c, frew) + ++rcsline; + savech_(c) + cacheget_(c) + } +# if large_memory + r.size = (char const*)cacheptr() - 1 - r.string; +# endif + for (;;) { + switch (ctab[c]) { + case NEWLN: + ++rcsline; + /* fall into */ + case SPACE: + cacheget_(c) + continue; + + default: break; + } + break; + } + if (frew) + aputc_(c, frew) + break; + } + break; + } + if (ctab[c] == Letter) { + for (kn = key; c && *kn==c; kn++) + GETC_(frew, c) + if (!*kn) + switch (ctab[c]) { + case DIGIT: case LETTER: case Letter: + case IDCHAR: case PERIOD: + break; + default: + nextc = c; + NextString = fstr_save(key); + nexttok = ID; + uncache(fin); + goto returnit; + } +# if !large_memory + { + register char const *ki; + for (ki=key; kistring; limit = tp + target->size; + for (;;) { + GETC_(frew, c) + switch (c) { + case '\n': + ++rcsline; + break; + case SDELIM: + GETC_(frew, c) + if (c != SDELIM) { + /* end of string */ + nextc=c; + r.string = target->string; + r.size = tp - r.string; + uncache(fin); + return r; + } + break; + } + if (tp == limit) + tp = bufenlarge(target, &limit); + *tp++ = c; + } +} + + + static char * +checkidentifier(id, delimiter, dotok) + register char *id; + int delimiter; + register int dotok; +/* Function: check whether the string starting at id is an */ +/* identifier and return a pointer to the delimiter*/ +/* after the identifier. White space, delim and 0 */ +/* are legal delimiters. Aborts the program if not*/ +/* a legal identifier. Useful for checking commands*/ +/* If !delim, the only delimiter is 0. */ +/* Allow '.' in identifier only if DOTOK is set. */ +{ + register char *temp; + register char c; + register char delim = delimiter; + int isid = false; + + temp = id; + for (;; id++) { + switch (ctab[(unsigned char)(c = *id)]) { + case IDCHAR: + case LETTER: + case Letter: + isid = true; + continue; + + case DIGIT: + continue; + + case PERIOD: + if (dotok) + continue; + break; + + default: + break; + } + break; + } + if ( ! isid + || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n'))) + ) { + /* append \0 to end of id before error message */ + while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim) + id++; + *id = '\0'; + faterror("invalid %s `%s'", + dotok ? "identifier" : "symbol", temp + ); + } + return id; +} + + char * +checkid(id, delimiter) + char *id; + int delimiter; +{ + return checkidentifier(id, delimiter, true); +} + + char * +checksym(sym, delimiter) + char *sym; + int delimiter; +{ + return checkidentifier(sym, delimiter, false); +} + + void +checksid(id) + char *id; +/* Check whether the string ID is an identifier. */ +{ + VOID checkid(id, 0); +} + + void +checkssym(sym) + char *sym; +{ + VOID checksym(sym, 0); +} + + +#if !large_memory +# define Iclose(f) fclose(f) +#else +# if !maps_memory + static int Iclose P((RILE *)); + static int + Iclose(f) + register RILE *f; + { + tfree(f->base); + f->base = 0; + return fclose(f->stream); + } +# else + static int Iclose P((RILE *)); + static int + Iclose(f) + register RILE *f; + { + (* f->deallocate) (f); + f->base = 0; + return close(f->fd); + } + +# if has_map_fd + static void map_fd_deallocate P((RILE *)); + static void + map_fd_deallocate(f) + register RILE *f; + { + if (vm_deallocate( + task_self(), + (vm_address_t) f->base, + (vm_size_t) (f->lim - f->base) + ) != KERN_SUCCESS) + efaterror("vm_deallocate"); + } +# endif +# if has_mmap + static void mmap_deallocate P((RILE *)); + static void + mmap_deallocate(f) + register RILE *f; + { + if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0) + efaterror("munmap"); + } +# endif + static void read_deallocate P((RILE *)); + static void + read_deallocate(f) + RILE *f; + { + tfree(f->base); + } + + static void nothing_to_deallocate P((RILE *)); + static void + nothing_to_deallocate(f) + RILE *f; + { + } +# endif +#endif + + +#if large_memory && maps_memory + static RILE *fd2_RILE P((int,char const*,struct stat*)); + static RILE * +fd2_RILE(fd, name, status) +#else + static RILE *fd2RILE P((int,char const*,char const*,struct stat*)); + static RILE * +fd2RILE(fd, name, type, status) + char const *type; +#endif + int fd; + char const *name; + register struct stat *status; +{ + struct stat st; + + if (!status) + status = &st; + if (fstat(fd, status) != 0) + efaterror(name); + if (!S_ISREG(status->st_mode)) { + error("`%s' is not a regular file", name); + VOID close(fd); + errno = EINVAL; + return 0; + } else { + +# if !(large_memory && maps_memory) + FILE *stream; + if (!(stream = fdopen(fd, type))) + efaterror(name); +# endif + +# if !large_memory + return stream; +# else +# define RILES 3 + { + static RILE rilebuf[RILES]; + + register RILE *f; + size_t s = status->st_size; + + if (s != status->st_size) + faterror("%s: too large", name); + for (f = rilebuf; f->base; f++) + if (f == rilebuf+RILES) + faterror("too many RILEs"); +# if maps_memory + f->deallocate = nothing_to_deallocate; +# endif + if (!s) { + static unsigned char nothing; + f->base = ¬hing; /* Any nonzero address will do. */ + } else { + f->base = 0; +# if has_map_fd + map_fd( + fd, (vm_offset_t)0, (vm_address_t*) &f->base, + TRUE, (vm_size_t)s + ); + f->deallocate = map_fd_deallocate; +# endif +# if has_mmap + if (!f->base) { + catchmmapints(); + f->base = (unsigned char *) mmap( + (char *)0, s, PROT_READ, MAP_SHARED, + fd, (off_t)0 + ); +# ifndef MAP_FAILED +# define MAP_FAILED (-1) +# endif + if (f->base == (unsigned char *) MAP_FAILED) + f->base = 0; + else { +# if has_NFS && mmap_signal + /* + * On many hosts, the superuser + * can mmap an NFS file it can't read. + * So access the first page now, and print + * a nice message if a bus error occurs. + */ + readAccessFilenameBuffer(name, f->base); +# endif + } + f->deallocate = mmap_deallocate; + } +# endif + if (!f->base) { + f->base = tnalloc(unsigned char, s); +# if maps_memory + { + /* + * We can't map the file into memory for some reason. + * Read it into main memory all at once; this is + * the simplest substitute for memory mapping. + */ + char *bufptr = (char *) f->base; + size_t bufsiz = s; + do { + ssize_t r = read(fd, bufptr, bufsiz); + switch (r) { + case -1: + efaterror(name); + + case 0: + /* The file must have shrunk! */ + status->st_size = s -= bufsiz; + bufsiz = 0; + break; + + default: + bufptr += r; + bufsiz -= r; + break; + } + } while (bufsiz); + if (lseek(fd, (off_t)0, SEEK_SET) == -1) + efaterror(name); + f->deallocate = read_deallocate; + } +# endif + } + } + f->ptr = f->base; + f->lim = f->base + s; + f->fd = fd; +# if !maps_memory + f->readlim = f->base; + f->stream = stream; +# endif + if_advise_access(s, f, MADV_SEQUENTIAL); + return f; + } +# endif + } +} + +#if !maps_memory && large_memory + int +Igetmore(f) + register RILE *f; +{ + register fread_type r; + register size_t s = f->lim - f->readlim; + + if (BUFSIZ < s) + s = BUFSIZ; + if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) { + testIerror(f->stream); + f->lim = f->readlim; /* The file might have shrunk! */ + return 0; + } + f->readlim += r; + return 1; +} +#endif + +#if has_madvise && has_mmap && large_memory + void +advise_access(f, advice) + register RILE *f; + int advice; +{ + if (f->deallocate == mmap_deallocate) + VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice); + /* Don't worry if madvise fails; it's only advisory. */ +} +#endif + + RILE * +#if large_memory && maps_memory +I_open(name, status) +#else +Iopen(name, type, status) + char const *type; +#endif + char const *name; + struct stat *status; +/* Open NAME for reading, yield its descriptor, and set *STATUS. */ +{ + int fd = fdSafer(open(name, O_RDONLY +# if OPEN_O_BINARY + | (strchr(type,'b') ? OPEN_O_BINARY : 0) +# endif + )); + + if (fd < 0) + return 0; +# if large_memory && maps_memory + return fd2_RILE(fd, name, status); +# else + return fd2RILE(fd, name, type, status); +# endif +} + + +static int Oerrloop; + + void +Oerror() +{ + if (Oerrloop) + exiterr(); + Oerrloop = true; + efaterror("output error"); +} + +void Ieof() { fatserror("unexpected end of file"); } +void Ierror() { efaterror("input error"); } +void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); } +void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); } + +void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); } +void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); } +void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; } +void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; } + +#if !large_memory + void +testIeof(f) + FILE *f; +{ + testIerror(f); + if (feof(f)) + Ieof(); +} +void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); } +#endif + +void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); } + +void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); } +void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); } +void oflush() +{ + if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop) + Oerror(); +} + + void +fatcleanup(already_newline) + int already_newline; +{ + VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid); + exiterr(); +} + + static void +startsay(s, t) + const char *s, *t; +{ + oflush(); + if (s) + aprintf(stderr, "%s: %s: %s", cmdid, s, t); + else + aprintf(stderr, "%s: %s", cmdid, t); +} + + static void +fatsay(s) + char const *s; +{ + startsay(s, ""); +} + + static void +errsay(s) + char const *s; +{ + fatsay(s); + nerror++; +} + + static void +warnsay(s) + char const *s; +{ + startsay(s, "warning: "); +} + +void eerror(s) char const *s; { enerror(errno,s); } + + void +enerror(e,s) + int e; + char const *s; +{ + errsay((char const*)0); + errno = e; + perror(s); + eflush(); +} + +void efaterror(s) char const *s; { enfaterror(errno,s); } + + void +enfaterror(e,s) + int e; + char const *s; +{ + fatsay((char const*)0); + errno = e; + perror(s); + fatcleanup(true); +} + +#if has_prototypes + void +error(char const *format,...) +#else + /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl +#endif +/* non-fatal error */ +{ + va_list args; + errsay((char const*)0); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + void +rcserror(char const *format,...) +#else + /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl +#endif +/* non-fatal RCS file error */ +{ + va_list args; + errsay(RCSname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + void +workerror(char const *format,...) +#else + /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl +#endif +/* non-fatal working file error */ +{ + va_list args; + errsay(workname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + void +fatserror(char const *format,...) +#else + /*VARARGS1*/ void + fatserror(format, va_alist) char const *format; va_dcl +#endif +/* fatal RCS file syntax error */ +{ + va_list args; + oflush(); + VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + void +faterror(char const *format,...) +#else + /*VARARGS1*/ void faterror(format, va_alist) + char const *format; va_dcl +#endif +/* fatal error, terminates program after cleanup */ +{ + va_list args; + fatsay((char const*)0); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + void +rcsfaterror(char const *format,...) +#else + /*VARARGS1*/ void rcsfaterror(format, va_alist) + char const *format; va_dcl +#endif +/* fatal RCS file error, terminates program after cleanup */ +{ + va_list args; + fatsay(RCSname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + void +warn(char const *format,...) +#else + /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl +#endif +/* warning */ +{ + va_list args; + if (!quietflag) { + warnsay((char *)0); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n', stderr); + eflush(); + } +} + +#if has_prototypes + void +rcswarn(char const *format,...) +#else + /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl +#endif +/* RCS file warning */ +{ + va_list args; + if (!quietflag) { + warnsay(RCSname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n', stderr); + eflush(); + } +} + +#if has_prototypes + void +workwarn(char const *format,...) +#else + /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl +#endif +/* working file warning */ +{ + va_list args; + if (!quietflag) { + warnsay(workname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n', stderr); + eflush(); + } +} + + void +redefined(c) + int c; +{ + warn("redefinition of -%c option", c); +} + +#if has_prototypes + void +diagnose(char const *format,...) +#else + /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl +#endif +/* prints a diagnostic message */ +/* Unlike the other routines, it does not append a newline. */ +/* This lets some callers suppress the newline, and is faster */ +/* in implementations that flush stderr just at the end of each printf. */ +{ + va_list args; + if (!quietflag) { + oflush(); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + eflush(); + } +} + + + + void +afputc(c, f) +/* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */ + int c; + register FILE *f; +{ + aputc_(c,f) +} + + + void +aputs(s, iop) + char const *s; + FILE *iop; +/* Function: Put string s on file iop, abort on error. + */ +{ +#if has_fputs + if (fputs(s, iop) < 0) + Oerror(); +#else + awrite(s, strlen(s), iop); +#endif +} + + + + void +#if has_prototypes +fvfprintf(FILE *stream, char const *format, va_list args) +#else + fvfprintf(stream,format,args) FILE *stream; char *format; va_list args; +#endif +/* like vfprintf, except abort program on error */ +{ +#if has_vfprintf + if (vfprintf(stream, format, args) < 0) + Oerror(); +#else +# if has__doprintf + _doprintf(stream, format, args); +# else +# if has__doprnt + _doprnt(format, args, stream); +# else + int *a = (int *)args; + VOID fprintf(stream, format, + a[0], a[1], a[2], a[3], a[4], + a[5], a[6], a[7], a[8], a[9] + ); +# endif +# endif + if (ferror(stream)) + Oerror(); +#endif +} + +#if has_prototypes + void +aprintf(FILE *iop, char const *fmt, ...) +#else + /*VARARGS2*/ void +aprintf(iop, fmt, va_alist) +FILE *iop; +char const *fmt; +va_dcl +#endif +/* Function: formatted output. Same as fprintf in stdio, + * but aborts program on error + */ +{ + va_list ap; + vararg_start(ap, fmt); + fvfprintf(iop, fmt, ap); + va_end(ap); +} + + + +#ifdef LEXDB +/* test program reading a stream of lexemes and printing the tokens. + */ + + + + int +main(argc,argv) +int argc; char * argv[]; +{ + cmdid="lextest"; + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { + faterror("can't open input file %s",argv[1]); + } + Lexinit(); + while (!eoflex()) { + switch (nexttok) { + + case ID: + VOID printf("ID: %s",NextString); + break; + + case NUM: + if (hshenter) + VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab); + else + VOID printf("NUM, unentered: %s",NextString); + hshenter = !hshenter; /*alternate between dates and numbers*/ + break; + + case COLON: + VOID printf("COLON"); break; + + case SEMI: + VOID printf("SEMI"); break; + + case STRING: + readstring(); + VOID printf("STRING"); break; + + case UNKN: + VOID printf("UNKN"); break; + + default: + VOID printf("DEFAULT"); break; + } + VOID printf(" | "); + nextlex(); + } + exitmain(EXIT_SUCCESS); +} + +void exiterr() { _exit(EXIT_FAILURE); } + + +#endif diff --git a/gnu/usr.bin/rcs/lib/rcsmap.c b/gnu/usr.bin/rcs/lib/rcsmap.c new file mode 100644 index 00000000000..89fb08daf36 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsmap.c @@ -0,0 +1,69 @@ +/* RCS map of character types */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1995 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +libId(mapId, "$FreeBSD$") + +/* map of character types */ +/* ISO 8859/1 (Latin-1) */ +enum tokens const ctab[] = { + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + SPACE, SPACE, NEWLN, SPACE, SPACE, SPACE, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + SPACE, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, PERIOD, IDCHAR, + DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, + DIGIT, DIGIT, COLON, SEMI, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + SBEGIN, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, IDCHAR, IDCHAR, IDCHAR, IDCHAR, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, IDCHAR, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, IDCHAR, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter +}; diff --git a/gnu/usr.bin/rcs/lib/rcsrev.c b/gnu/usr.bin/rcs/lib/rcsrev.c new file mode 100644 index 00000000000..12c6c43006a --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsrev.c @@ -0,0 +1,911 @@ +/* Handle RCS revision numbers. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.10 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.9 1995/06/01 16:23:43 eggert + * (cmpdate, normalizeyear): New functions work around MKS RCS incompatibility. + * (cmpnum, compartial): s[d] -> *(s+d) to work around Cray compiler bug. + * (genrevs, genbranch): cmpnum -> cmpdate + * + * Revision 5.8 1994/03/17 14:05:48 eggert + * Remove lint. + * + * Revision 5.7 1993/11/09 17:40:15 eggert + * Fix format string typos. + * + * Revision 5.6 1993/11/03 17:42:27 eggert + * Revision number `.N' now stands for `D.N', where D is the default branch. + * Add -z. Improve quality of diagnostics. Add `namedrev' for Name support. + * + * Revision 5.5 1992/07/28 16:12:44 eggert + * Identifiers may now start with a digit. Avoid `unsigned'. + * + * Revision 5.4 1992/01/06 02:42:34 eggert + * while (E) ; -> while (E) continue; + * + * Revision 5.3 1991/08/19 03:13:55 eggert + * Add `-r$', `-rB.'. Remove botches like `' from messages. Tune. + * + * Revision 5.2 1991/04/21 11:58:28 eggert + * Add tiprev(). + * + * Revision 5.1 1991/02/25 07:12:43 eggert + * Avoid overflow when comparing revision numbers. + * + * Revision 5.0 1990/08/22 08:13:43 eggert + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. Tune. + * Remove possibility of an internal error. Remove lint. + * + * Revision 4.5 89/05/01 15:13:22 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 87/12/18 11:45:22 narten + * more lint cleanups. Also, the NOTREACHED comment is no longer necessary, + * since there's now a return value there with a value. (Guy Harris) + * + * Revision 4.3 87/10/18 10:38:42 narten + * Updating version numbers. Changes relative to version 1.1 actually + * relative to 4.1 + * + * Revision 1.3 87/09/24 14:00:37 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:37 jenkins + * Port to suns + * + * Revision 4.1 83/03/25 21:10:45 wft + * Only changed $Header to $Id. + * + * Revision 3.4 82/12/04 13:24:08 wft + * Replaced getdelta() with gettree(). + * + * Revision 3.3 82/11/28 21:33:15 wft + * fixed compartial() and compnum() for nil-parameters; fixed nils + * in error messages. Testprogram output shortenend. + * + * Revision 3.2 82/10/18 21:19:47 wft + * renamed compnum->cmpnum, compnumfld->cmpnumfld, + * numericrevno->numricrevno. + * + * Revision 3.1 82/10/11 19:46:09 wft + * changed expandsym() to check for source==nil; returns zero length string + * in that case. + */ + +#include "rcsbase.h" + +libId(revId, "$FreeBSD$") + +static char const *branchtip P((char const*)); +static char const *lookupsym P((char const*)); +static char const *normalizeyear P((char const*,char[5])); +static struct hshentry *genbranch P((struct hshentry const*,char const*,int,char const*,char const*,char const*,struct hshentries**)); +static void absent P((char const*,int)); +static void cantfindbranch P((char const*,char const[datesize],char const*,char const*)); +static void store1 P((struct hshentries***,struct hshentry*)); + + + + int +countnumflds(s) + char const *s; +/* Given a pointer s to a dotted number (date or revision number), + * countnumflds returns the number of digitfields in s. + */ +{ + register char const *sp; + register int count; + if (!(sp=s) || !*sp) + return 0; + count = 1; + do { + if (*sp++ == '.') count++; + } while (*sp); + return(count); +} + + void +getbranchno(revno,branchno) + char const *revno; + struct buf *branchno; +/* Given a revision number revno, getbranchno copies the number of the branch + * on which revno is into branchno. If revno itself is a branch number, + * it is copied unchanged. + */ +{ + register int numflds; + register char *tp; + + bufscpy(branchno, revno); + numflds=countnumflds(revno); + if (!(numflds & 1)) { + tp = branchno->string; + while (--numflds) + while (*tp++ != '.') + continue; + *(tp-1)='\0'; + } +} + + + +int cmpnum(num1, num2) + char const *num1, *num2; +/* compares the two dotted numbers num1 and num2 lexicographically + * by field. Individual fields are compared numerically. + * returns <0, 0, >0 if num1num2, resp. + * omitted fields are assumed to be higher than the existing ones. +*/ +{ + register char const *s1, *s2; + register size_t d1, d2; + register int r; + + s1 = num1 ? num1 : ""; + s2 = num2 ? num2 : ""; + + for (;;) { + /* Give precedence to shorter one. */ + if (!*s1) + return (unsigned char)*s2; + if (!*s2) + return -1; + + /* Strip leading zeros, then find number of digits. */ + while (*s1=='0') ++s1; + while (*s2=='0') ++s2; + for (d1=0; isdigit(*(s1+d1)); d1++) continue; + for (d2=0; isdigit(*(s2+d2)); d2++) continue; + + /* Do not convert to integer; it might overflow! */ + if (d1 != d2) + return d1string; + while (length) { + while (*r1!='.' && *r1) + ++r1; + ++r1; + length--; + } + /* eliminate last '.'*/ + *(r1-1)='\0'; + return rev1->string; +} + + + + + static void +store1(store, next) + struct hshentries ***store; + struct hshentry *next; +/* + * Allocate a new list node that addresses NEXT. + * Append it to the list that **STORE is the end pointer of. + */ +{ + register struct hshentries *p; + + p = ftalloc(struct hshentries); + p->first = next; + **store = p; + *store = &p->rest; +} + +struct hshentry * genrevs(revno,date,author,state,store) + char const *revno, *date, *author, *state; + struct hshentries **store; +/* Function: finds the deltas needed for reconstructing the + * revision given by revno, date, author, and state, and stores pointers + * to these deltas into a list whose starting address is given by store. + * The last delta (target delta) is returned. + * If the proper delta could not be found, 0 is returned. + */ +{ + int length; + register struct hshentry * next; + int result; + char const *branchnum; + struct buf t; + char datebuf[datesize + zonelenmax]; + + bufautobegin(&t); + + if (!(next = Head)) { + rcserror("RCS file empty"); + goto norev; + } + + length = countnumflds(revno); + + if (length >= 1) { + /* at least one field; find branch exactly */ + while ((result=cmpnumfld(revno,next->num,1)) < 0) { + store1(&store, next); + next = next->next; + if (!next) { + rcserror("branch number %s too low", partialno(&t,revno,1)); + goto norev; + } + } + + if (result>0) { + absent(revno, 1); + goto norev; + } + } + if (length<=1){ + /* pick latest one on given branch */ + branchnum = next->num; /* works even for empty revno*/ + while (next && + cmpnumfld(branchnum,next->num,1) == 0 && + ( + (date && cmpdate(date,next->date) < 0) || + (author && strcmp(author,next->author) != 0) || + (state && strcmp(state,next->state) != 0) + ) + ) + { + store1(&store, next); + next=next->next; + } + if (!next || + (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { + cantfindbranch( + length ? revno : partialno(&t,branchnum,1), + date, author, state + ); + goto norev; + } else { + store1(&store, next); + } + *store = 0; + return next; + } + + /* length >=2 */ + /* find revision; may go low if length==2*/ + while ((result=cmpnumfld(revno,next->num,2)) < 0 && + (cmpnumfld(revno,next->num,1)==0) ) { + store1(&store, next); + next = next->next; + if (!next) + break; + } + + if (!next || cmpnumfld(revno,next->num,1) != 0) { + rcserror("revision number %s too low", partialno(&t,revno,2)); + goto norev; + } + if ((length>2) && (result!=0)) { + absent(revno, 2); + goto norev; + } + + /* print last one */ + store1(&store, next); + + if (length>2) + return genbranch(next,revno,length,date,author,state,store); + else { /* length == 2*/ + if (date && cmpdate(date,next->date)<0) { + rcserror("Revision %s has date %s.", + next->num, + date2str(next->date, datebuf) + ); + return 0; + } + if (author && strcmp(author,next->author)!=0) { + rcserror("Revision %s has author %s.", + next->num, next->author + ); + return 0; + } + if (state && strcmp(state,next->state)!=0) { + rcserror("Revision %s has state %s.", + next->num, + next->state ? next->state : "" + ); + return 0; + } + *store = 0; + return next; + } + + norev: + bufautoend(&t); + return 0; +} + + + + + static struct hshentry * +genbranch(bpoint, revno, length, date, author, state, store) + struct hshentry const *bpoint; + char const *revno; + int length; + char const *date, *author, *state; + struct hshentries **store; +/* Function: given a branchpoint, a revision number, date, author, and state, + * genbranch finds the deltas necessary to reconstruct the given revision + * from the branch point on. + * Pointers to the found deltas are stored in a list beginning with store. + * revno must be on a side branch. + * Return 0 on error. + */ +{ + int field; + register struct hshentry * next, * trail; + register struct branchhead const *bhead; + int result; + struct buf t; + char datebuf[datesize + zonelenmax]; + + field = 3; + bhead = bpoint->branches; + + do { + if (!bhead) { + bufautobegin(&t); + rcserror("no side branches present for %s", + partialno(&t,revno,field-1) + ); + bufautoend(&t); + return 0; + } + + /*find branch head*/ + /*branches are arranged in increasing order*/ + while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) { + bhead = bhead->nextbranch; + if (!bhead) { + bufautobegin(&t); + rcserror("branch number %s too high", + partialno(&t,revno,field) + ); + bufautoend(&t); + return 0; + } + } + + if (result<0) { + absent(revno, field); + return 0; + } + + next = bhead->hsh; + if (length==field) { + /* pick latest one on that branch */ + trail = 0; + do { if ((!date || cmpdate(date,next->date)>=0) && + (!author || strcmp(author,next->author)==0) && + (!state || strcmp(state,next->state)==0) + ) trail = next; + next=next->next; + } while (next); + + if (!trail) { + cantfindbranch(revno, date, author, state); + return 0; + } else { /* print up to last one suitable */ + next = bhead->hsh; + while (next!=trail) { + store1(&store, next); + next=next->next; + } + store1(&store, next); + } + *store = 0; + return next; + } + + /* length > field */ + /* find revision */ + /* check low */ + if (cmpnumfld(revno,next->num,field+1)<0) { + bufautobegin(&t); + rcserror("revision number %s too low", + partialno(&t,revno,field+1) + ); + bufautoend(&t); + return 0; + } + do { + store1(&store, next); + trail = next; + next = next->next; + } while (next && cmpnumfld(revno,next->num,field+1)>=0); + + if ((length>field+1) && /*need exact hit */ + (cmpnumfld(revno,trail->num,field+1) !=0)){ + absent(revno, field+1); + return 0; + } + if (length == field+1) { + if (date && cmpdate(date,trail->date)<0) { + rcserror("Revision %s has date %s.", + trail->num, + date2str(trail->date, datebuf) + ); + return 0; + } + if (author && strcmp(author,trail->author)!=0) { + rcserror("Revision %s has author %s.", + trail->num, trail->author + ); + return 0; + } + if (state && strcmp(state,trail->state)!=0) { + rcserror("Revision %s has state %s.", + trail->num, + trail->state ? trail->state : "" + ); + return 0; + } + } + bhead = trail->branches; + + } while ((field+=2) <= length); + *store = 0; + return trail; +} + + + static char const * +lookupsym(id) + char const *id; +/* Function: looks up id in the list of symbolic names starting + * with pointer SYMBOLS, and returns a pointer to the corresponding + * revision number. Return 0 if not present. + */ +{ + register struct assoc const *next; + for (next = Symbols; next; next = next->nextassoc) + if (strcmp(id, next->symbol)==0) + return next->num; + return 0; +} + +int expandsym(source, target) + char const *source; + struct buf *target; +/* Function: Source points to a revision number. Expandsym copies + * the number to target, but replaces all symbolic fields in the + * source number with their numeric values. + * Expand a branch followed by `.' to the latest revision on that branch. + * Ignore `.' after a revision. Remove leading zeros. + * returns false on error; + */ +{ + return fexpandsym(source, target, (RILE*)0); +} + + int +fexpandsym(source, target, fp) + char const *source; + struct buf *target; + RILE *fp; +/* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */ +{ + register char const *sp, *bp; + register char *tp; + char const *tlim; + int dots; + + sp = source; + bufalloc(target, 1); + tp = target->string; + if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */ + *tp='\0'; + return true; + } + if (sp[0] == KDELIM && !sp[1]) { + if (!getoldkeys(fp)) + return false; + if (!*prevrev.string) { + workerror("working file lacks revision number"); + return false; + } + bufscpy(target, prevrev.string); + return true; + } + tlim = tp + target->size; + dots = 0; + + for (;;) { + register char *p = tp; + size_t s = tp - target->string; + int id = false; + for (;;) { + switch (ctab[(unsigned char)*sp]) { + case IDCHAR: + case LETTER: + case Letter: + id = true; + /* fall into */ + case DIGIT: + if (tlim <= p) + p = bufenlarge(target, &tlim); + *p++ = *sp++; + continue; + + default: + break; + } + break; + } + if (tlim <= p) + p = bufenlarge(target, &tlim); + *p = 0; + tp = target->string + s; + + if (id) { + bp = lookupsym(tp); + if (!bp) { + rcserror("Symbolic name `%s' is undefined.",tp); + return false; + } + } else { + /* skip leading zeros */ + for (bp = tp; *bp=='0' && isdigit(bp[1]); bp++) + continue; + + if (!*bp) + if (s || *sp!='.') + break; + else { + /* Insert default branch before initial `.'. */ + char const *b; + if (Dbranch) + b = Dbranch; + else if (Head) + b = Head->num; + else + break; + getbranchno(b, target); + bp = tp = target->string; + tlim = tp + target->size; + } + } + + while ((*tp++ = *bp++)) + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + + switch (*sp++) { + case '\0': + return true; + + case '.': + if (!*sp) { + if (dots & 1) + break; + if (!(bp = branchtip(target->string))) + return false; + bufscpy(target, bp); + return true; + } + ++dots; + tp[-1] = '.'; + continue; + } + break; + } + + rcserror("improper revision number: %s", source); + return false; +} + + char const * +namedrev(name, delta) + char const *name; + struct hshentry *delta; +/* Yield NAME if it names DELTA, 0 otherwise. */ +{ + if (name) { + char const *id = 0, *p, *val; + for (p = name; ; p++) + switch (ctab[(unsigned char)*p]) { + case IDCHAR: + case LETTER: + case Letter: + id = name; + break; + + case DIGIT: + break; + + case UNKN: + if (!*p && id && + (val = lookupsym(id)) && + strcmp(val, delta->num) == 0 + ) + return id; + /* fall into */ + default: + return 0; + } + } + return 0; +} + + static char const * +branchtip(branch) + char const *branch; +{ + struct hshentry *h; + struct hshentries *hs; + + h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs); + return h ? h->num : (char const*)0; +} + + char const * +tiprev() +{ + return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0; +} + + + +#ifdef REVTEST + +/* +* Test the routines that generate a sequence of delta numbers +* needed to regenerate a given delta. +*/ + +char const cmdid[] = "revtest"; + + int +main(argc,argv) +int argc; char * argv[]; +{ + static struct buf numricrevno; + char symrevno[100]; /* used for input of revision numbers */ + char author[20]; + char state[20]; + char date[20]; + struct hshentries *gendeltas; + struct hshentry * target; + int i; + + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { + faterror("can't open input file %s", argv[1]); + } + Lexinit(); + getadmin(); + + gettree(); + + getdesc(false); + + do { + /* all output goes to stderr, to have diagnostics and */ + /* errors in sequence. */ + aputs("\nEnter revision number or or '.': ",stderr); + if (!fgets(symrevno, 100, stdin)) break; + if (*symrevno == '.') break; + aprintf(stderr,"%s;\n",symrevno); + expandsym(symrevno,&numricrevno); + aprintf(stderr,"expanded number: %s; ",numricrevno.string); + aprintf(stderr,"Date: "); + fgets(date, 20, stdin); aprintf(stderr,"%s; ",date); + aprintf(stderr,"Author: "); + fgets(author, 20, stdin); aprintf(stderr,"%s; ",author); + aprintf(stderr,"State: "); + fgets(state, 20, stdin); aprintf(stderr, "%s;\n", state); + target = genrevs(numricrevno.string, *date?date:(char *)0, *author?author:(char *)0, + *state?state:(char*)0, &gendeltas); + if (target) { + while (gendeltas) { + aprintf(stderr,"%s\n",gendeltas->first->num); + gendeltas = gendeltas->next; + } + } + } while (true); + aprintf(stderr,"done\n"); + exitmain(EXIT_SUCCESS); +} + +void exiterr() { _exit(EXIT_FAILURE); } + +#endif diff --git a/gnu/usr.bin/rcs/lib/rcssyn.c b/gnu/usr.bin/rcs/lib/rcssyn.c new file mode 100644 index 00000000000..07f155bfb8e --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcssyn.c @@ -0,0 +1,681 @@ +/* RCS file syntactic analysis */ + +/****************************************************************************** + * Syntax Analysis. + * Keyword table + * Testprogram: define SYNTEST + * Compatibility with Release 2: define COMPAT2=1 + ****************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.15 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.14 1995/06/01 16:23:43 eggert + * (expand_names): Add "b" for -kb. + * (getdelta): Don't strip leading "19" from MKS RCS dates; see cmpdate. + * + * Revision 5.13 1994/03/20 04:52:58 eggert + * Remove lint. + * + * Revision 5.12 1993/11/03 17:42:27 eggert + * Parse MKS RCS dates; ignore \r in diff control lines. + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.11 1992/07/28 16:12:44 eggert + * Avoid `unsigned'. Statement macro names now end in _. + * + * Revision 5.10 1992/01/24 18:44:19 eggert + * Move put routines to rcsgen.c. + * + * Revision 5.9 1992/01/06 02:42:34 eggert + * ULONG_MAX/10 -> ULONG_MAX_OVER_10 + * while (E) ; -> while (E) continue; + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Tune. + * + * Revision 5.7 1991/04/21 11:58:29 eggert + * Disambiguate names on shortname hosts. + * Fix errno bug. Add MS-DOS support. + * + * Revision 5.6 1991/02/28 19:18:51 eggert + * Fix null termination bug in reporting keyword expansion. + * + * Revision 5.5 1991/02/25 07:12:44 eggert + * Check diff output more carefully; avoid overflow. + * + * Revision 5.4 1990/11/01 05:28:48 eggert + * When ignoring unknown phrases, copy them to the output RCS file. + * Permit arbitrary data in logs and comment leaders. + * Don't check for nontext on initial checkin. + * + * Revision 5.3 1990/09/20 07:58:32 eggert + * Remove the test for non-text bytes; it caused more pain than it cured. + * + * Revision 5.2 1990/09/04 08:02:30 eggert + * Parse RCS files with no revisions. + * Don't strip leading white space from diff commands. Count RCS lines better. + * + * Revision 5.1 1990/08/29 07:14:06 eggert + * Add -kkvl. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:13:44 eggert + * Try to parse future RCS formats without barfing. + * Add -k. Don't require final newline. + * Remove compile-time limits; use malloc instead. + * Don't output branch keyword if there's no default branch, + * because RCS version 3 doesn't understand it. + * Tune. Remove lint. + * Add support for ISO 8859. Ansify and Posixate. + * Check that a newly checked-in file is acceptable as input to 'diff'. + * Check diff's output. + * + * Revision 4.6 89/05/01 15:13:32 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/09 19:13:21 eggert + * Allow cc -R; remove lint. + * + * Revision 4.4 87/12/18 11:46:16 narten + * more lint cleanups (Guy Harris) + * + * Revision 4.3 87/10/18 10:39:36 narten + * Updating version numbers. Changes relative to 1.1 actually relative to + * 4.1 + * + * Revision 1.3 87/09/24 14:00:49 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:40 jenkins + * Port to suns + * + * Revision 4.1 83/03/28 11:38:49 wft + * Added parsing and printing of default branch. + * + * Revision 3.6 83/01/15 17:46:50 wft + * Changed readdelta() to initialize selector and log-pointer. + * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM. + * + * Revision 3.5 82/12/08 21:58:58 wft + * renamed Commentleader to Commleader. + * + * Revision 3.4 82/12/04 13:24:40 wft + * Added routine gettree(), which updates keeplock after reading the + * delta tree. + * + * Revision 3.3 82/11/28 21:30:11 wft + * Reading and printing of Suffix removed; version COMPAT2 skips the + * Suffix for files of release 2 format. Fixed problems with printing nil. + * + * Revision 3.2 82/10/18 21:18:25 wft + * renamed putdeltatext to putdtext. + * + * Revision 3.1 82/10/11 19:45:11 wft + * made sure getc() returns into an integer. + */ + + + +/* version COMPAT2 reads files of the format of release 2 and 3, but + * generates files of release 3 format. Need not be defined if no + * old RCS files generated with release 2 exist. + */ + +#include "rcsbase.h" + +libId(synId, "$FreeBSD$") + +static char const *getkeyval P((char const*,enum tokens,int)); +static int getdelta P((void)); +static int strn2expmode P((char const*,size_t)); +static struct hshentry *getdnum P((void)); +static void badDiffOutput P((char const*)) exiting; +static void diffLineNumberTooLarge P((char const*)) exiting; +static void getsemi P((char const*)); + +/* keyword table */ + +char const + Kaccess[] = "access", + Kauthor[] = "author", + Kbranch[] = "branch", + Kcomment[] = "comment", + Kdate[] = "date", + Kdesc[] = "desc", + Kexpand[] = "expand", + Khead[] = "head", + Klocks[] = "locks", + Klog[] = "log", + Knext[] = "next", + Kstate[] = "state", + Kstrict[] = "strict", + Ksymbols[] = "symbols", + Ktext[] = "text"; + +static char const +#if COMPAT2 + Ksuffix[] = "suffix", +#endif + K_branches[]= "branches"; + +static struct buf Commleader; +struct cbuf Comment; +struct cbuf Ignored; +struct access * AccessList; +struct assoc * Symbols; +struct rcslock *Locks; +int Expand; +int StrictLocks; +struct hshentry * Head; +char const * Dbranch; +int TotalDeltas; + + + static void +getsemi(key) + char const *key; +/* Get a semicolon to finish off a phrase started by KEY. */ +{ + if (!getlex(SEMI)) + fatserror("missing ';' after '%s'", key); +} + + static struct hshentry * +getdnum() +/* Get a delta number. */ +{ + register struct hshentry *delta = getnum(); + if (delta && countnumflds(delta->num)&1) + fatserror("%s isn't a delta number", delta->num); + return delta; +} + + + void +getadmin() +/* Read an and initialize the appropriate global variables. */ +{ + register char const *id; + struct access * newaccess; + struct assoc * newassoc; + struct rcslock *newlock; + struct hshentry * delta; + struct access **LastAccess; + struct assoc **LastSymbol; + struct rcslock **LastLock; + struct buf b; + struct cbuf cb; + + TotalDeltas=0; + + getkey(Khead); + Head = getdnum(); + getsemi(Khead); + + Dbranch = 0; + if (getkeyopt(Kbranch)) { + if ((delta = getnum())) + Dbranch = delta->num; + getsemi(Kbranch); + } + + +#if COMPAT2 + /* read suffix. Only in release 2 format */ + if (getkeyopt(Ksuffix)) { + if (nexttok==STRING) { + readstring(); nextlex(); /* Throw away the suffix. */ + } else if (nexttok==ID) { + nextlex(); + } + getsemi(Ksuffix); + } +#endif + + getkey(Kaccess); + LastAccess = &AccessList; + while ((id = getid())) { + newaccess = ftalloc(struct access); + newaccess->login = id; + *LastAccess = newaccess; + LastAccess = &newaccess->nextaccess; + } + *LastAccess = 0; + getsemi(Kaccess); + + getkey(Ksymbols); + LastSymbol = &Symbols; + while ((id = getid())) { + if (!getlex(COLON)) + fatserror("missing ':' in symbolic name definition"); + if (!(delta=getnum())) { + fatserror("missing number in symbolic name definition"); + } else { /*add new pair to association list*/ + newassoc = ftalloc(struct assoc); + newassoc->symbol=id; + newassoc->num = delta->num; + *LastSymbol = newassoc; + LastSymbol = &newassoc->nextassoc; + } + } + *LastSymbol = 0; + getsemi(Ksymbols); + + getkey(Klocks); + LastLock = &Locks; + while ((id = getid())) { + if (!getlex(COLON)) + fatserror("missing ':' in lock"); + if (!(delta=getdnum())) { + fatserror("missing number in lock"); + } else { /*add new pair to lock list*/ + newlock = ftalloc(struct rcslock); + newlock->login=id; + newlock->delta=delta; + *LastLock = newlock; + LastLock = &newlock->nextlock; + } + } + *LastLock = 0; + getsemi(Klocks); + + if ((StrictLocks = getkeyopt(Kstrict))) + getsemi(Kstrict); + + clear_buf(&Comment); + if (getkeyopt(Kcomment)) { + if (nexttok==STRING) { + Comment = savestring(&Commleader); + nextlex(); + } + getsemi(Kcomment); + } + + Expand = KEYVAL_EXPAND; + if (getkeyopt(Kexpand)) { + if (nexttok==STRING) { + bufautobegin(&b); + cb = savestring(&b); + if ((Expand = strn2expmode(cb.string,cb.size)) < 0) + fatserror("unknown expand mode %.*s", + (int)cb.size, cb.string + ); + bufautoend(&b); + nextlex(); + } + getsemi(Kexpand); + } + Ignored = getphrases(Kdesc); +} + +char const *const expand_names[] = { + /* These must agree with *_EXPAND in rcsbase.h. */ + "kv", "kvl", "k", "v", "o", "b", + 0 +}; + + int +str2expmode(s) + char const *s; +/* Yield expand mode corresponding to S, or -1 if bad. */ +{ + return strn2expmode(s, strlen(s)); +} + + static int +strn2expmode(s, n) + char const *s; + size_t n; +{ + char const *const *p; + + for (p = expand_names; *p; ++p) + if (memcmp(*p,s,n) == 0 && !(*p)[n]) + return p - expand_names; + return -1; +} + + + void +ignorephrases(key) + const char *key; +/* +* Ignore a series of phrases that do not start with KEY. +* Stop when the next phrase starts with a token that is not an identifier, +* or is KEY. +*/ +{ + for (;;) { + nextlex(); + if (nexttok != ID || strcmp(NextString,key) == 0) + break; + warnignore(); + hshenter=false; + for (;; nextlex()) { + switch (nexttok) { + case SEMI: hshenter=true; break; + case ID: + case NUM: ffree1(NextString); continue; + case STRING: readstring(); continue; + default: continue; + } + break; + } + } +} + + + static int +getdelta() +/* Function: reads a delta block. + * returns false if the current block does not start with a number. + */ +{ + register struct hshentry * Delta, * num; + struct branchhead **LastBranch, *NewBranch; + + if (!(Delta = getdnum())) + return false; + + hshenter = false; /*Don't enter dates into hashtable*/ + Delta->date = getkeyval(Kdate, NUM, false); + hshenter=true; /*reset hshenter for revision numbers.*/ + + Delta->author = getkeyval(Kauthor, ID, false); + + Delta->state = getkeyval(Kstate, ID, true); + + getkey(K_branches); + LastBranch = &Delta->branches; + while ((num = getdnum())) { + NewBranch = ftalloc(struct branchhead); + NewBranch->hsh = num; + *LastBranch = NewBranch; + LastBranch = &NewBranch->nextbranch; + } + *LastBranch = 0; + getsemi(K_branches); + + getkey(Knext); + Delta->next = num = getdnum(); + getsemi(Knext); + Delta->lockedby = 0; + Delta->log.string = 0; + Delta->selector = true; + Delta->ig = getphrases(Kdesc); + TotalDeltas++; + return (true); +} + + + void +gettree() +/* Function: Reads in the delta tree with getdelta(), then + * updates the lockedby fields. + */ +{ + struct rcslock const *currlock; + + while (getdelta()) + continue; + currlock=Locks; + while (currlock) { + currlock->delta->lockedby = currlock->login; + currlock = currlock->nextlock; + } +} + + + void +getdesc(prdesc) +int prdesc; +/* Function: read in descriptive text + * nexttok is not advanced afterwards. + * If prdesc is set, the text is printed to stdout. + */ +{ + + getkeystring(Kdesc); + if (prdesc) + printstring(); /*echo string*/ + else readstring(); /*skip string*/ +} + + + + + + + static char const * +getkeyval(keyword, token, optional) + char const *keyword; + enum tokens token; + int optional; +/* reads a pair of the form + * ; + * where token is one of or . optional indicates whether + * is optional. A pointer to + * the actual character string of or is returned. + */ +{ + register char const *val = 0; + + getkey(keyword); + if (nexttok==token) { + val = NextString; + nextlex(); + } else { + if (!optional) + fatserror("missing %s", keyword); + } + getsemi(keyword); + return(val); +} + + + void +unexpected_EOF() +{ + rcsfaterror("unexpected EOF in diff output"); +} + + void +initdiffcmd(dc) + register struct diffcmd *dc; +/* Initialize *dc suitably for getdiffcmd(). */ +{ + dc->adprev = 0; + dc->dafter = 0; +} + + static void +badDiffOutput(buf) + char const *buf; +{ + rcsfaterror("bad diff output line: %s", buf); +} + + static void +diffLineNumberTooLarge(buf) + char const *buf; +{ + rcsfaterror("diff line number too large: %s", buf); +} + + int +getdiffcmd(finfile, delimiter, foutfile, dc) + RILE *finfile; + FILE *foutfile; + int delimiter; + struct diffcmd *dc; +/* Get a editing command output by 'diff -n' from fin. + * The input is delimited by SDELIM if delimiter is set, EOF otherwise. + * Copy a clean version of the command to fout (if nonnull). + * Yield 0 for 'd', 1 for 'a', and -1 for EOF. + * Store the command's line number and length into dc->line1 and dc->nlines. + * Keep dc->adprev and dc->dafter up to date. + */ +{ + register int c; + declarecache; + register FILE *fout; + register char *p; + register RILE *fin; + long line1, nlines, t; + char buf[BUFSIZ]; + + fin = finfile; + fout = foutfile; + setupcache(fin); cache(fin); + cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } ) + if (delimiter) { + if (c==SDELIM) { + cacheget_(c) + if (c==SDELIM) { + buf[0] = c; + buf[1] = 0; + badDiffOutput(buf); + } + uncache(fin); + nextc = c; + if (fout) + aprintf(fout, "%c%c", SDELIM, c); + return -1; + } + } + p = buf; + do { + if (buf+BUFSIZ-2 <= p) { + rcsfaterror("diff output command line too long"); + } + *p++ = c; + cachegeteof_(c, unexpected_EOF();) + } while (c != '\n'); + uncache(fin); + if (delimiter) + ++rcsline; + *p = '\0'; + for (p = buf+1; (c = *p++) == ' '; ) + continue; + line1 = 0; + while (isdigit(c)) { + if ( + LONG_MAX/10 < line1 || + (t = line1 * 10, (line1 = t + (c - '0')) < t) + ) + diffLineNumberTooLarge(buf); + c = *p++; + } + while (c == ' ') + c = *p++; + nlines = 0; + while (isdigit(c)) { + if ( + LONG_MAX/10 < nlines || + (t = nlines * 10, (nlines = t + (c - '0')) < t) + ) + diffLineNumberTooLarge(buf); + c = *p++; + } + if (c == '\r') + c = *p++; + if (c || !nlines) { + badDiffOutput(buf); + } + if (line1+nlines < line1) + diffLineNumberTooLarge(buf); + switch (buf[0]) { + case 'a': + if (line1 < dc->adprev) { + rcsfaterror("backward insertion in diff output: %s", buf); + } + dc->adprev = line1 + 1; + break; + case 'd': + if (line1 < dc->adprev || line1 < dc->dafter) { + rcsfaterror("backward deletion in diff output: %s", buf); + } + dc->adprev = line1; + dc->dafter = line1 + nlines; + break; + default: + badDiffOutput(buf); + } + if (fout) { + aprintf(fout, "%s\n", buf); + } + dc->line1 = line1; + dc->nlines = nlines; + return buf[0] == 'a'; +} + + + +#ifdef SYNTEST + +/* Input an RCS file and print its internal data structures. */ + +char const cmdid[] = "syntest"; + + int +main(argc,argv) +int argc; char * argv[]; +{ + + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) { + faterror("can't open input file %s", argv[1]); + } + Lexinit(); + getadmin(); + fdlock = STDOUT_FILENO; + putadmin(); + + gettree(); + + getdesc(true); + + nextlex(); + + if (!eoflex()) { + fatserror("expecting EOF"); + } + exitmain(EXIT_SUCCESS); +} + +void exiterr() { _exit(EXIT_FAILURE); } + +#endif diff --git a/gnu/usr.bin/rcs/lib/rcstime.c b/gnu/usr.bin/rcs/lib/rcstime.c new file mode 100644 index 00000000000..cfd466096ff --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcstime.c @@ -0,0 +1,191 @@ +/* Convert between RCS time format and Posix and/or C formats. */ + +/* Copyright 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" +#include "partime.h" +#include "maketime.h" + +libId(rcstimeId, "$FreeBSD$") + +static long zone_offset; /* seconds east of UTC, or TM_LOCAL_ZONE */ +static int use_zone_offset; /* if zero, use UTC without zone indication */ + +/* +* Convert Unix time to RCS format. +* For compatibility with older versions of RCS, +* dates from 1900 through 1999 are stored without the leading "19". +*/ + void +time2date(unixtime,date) + time_t unixtime; + char date[datesize]; +{ + register struct tm const *tm = time2tm(unixtime, RCSversiontm_year + ((unsigned)tm->tm_year < 100 ? 0 : 1900), + tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec + ); +} + +/* Like str2time, except die if an error was found. */ +static time_t str2time_checked P((char const*,time_t,long)); + static time_t +str2time_checked(source, default_time, default_zone) + char const *source; + time_t default_time; + long default_zone; +{ + time_t t = str2time(source, default_time, default_zone); + if (t == -1) + faterror("unknown date/time: %s", source); + return t; +} + +/* +* Parse a free-format date in SOURCE, convert it +* into RCS internal format, and store the result into TARGET. +*/ + void +str2date(source, target) + char const *source; + char target[datesize]; +{ + time2date( + str2time_checked(source, now(), + use_zone_offset ? zone_offset + : RCSversiontm_year + 1900, + z->tm_mon + 1, z->tm_mday, z->tm_hour, z->tm_min, z->tm_sec, + c, (int) (zone / (60*60)) + ); + if ((non_hour = zone % (60*60))) { +# if has_printf_dot + static char const fmt[] = ":%.2d"; +# else + static char const fmt[] = ":%02d"; +# endif + VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour / 60); + if ((non_hour %= 60)) + VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour); + } + } + return datebuf; +} diff --git a/gnu/usr.bin/rcs/lib/rcsutil.c b/gnu/usr.bin/rcs/lib/rcsutil.c new file mode 100644 index 00000000000..e10afff6c20 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsutil.c @@ -0,0 +1,1398 @@ +/* RCS utility functions */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* + * Revision 5.20 1995/06/16 06:19:24 eggert + * (catchsig): Remove `return'. + * Update FSF address. + * + * Revision 5.19 1995/06/02 18:19:00 eggert + * (catchsigaction): New name for `catchsig', for sa_sigaction signature. + * Use nRCS even if !has_psiginfo, to remove unused variable warning. + * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction. + * Use ENOTSUP only if defined. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo, + * to determine whether to use SA_SIGINFO feature, + * but also check at runtime whether the feature works. + * (catchsig): If an mmap_signal occurs, report the affected file name. + * (unsupported_SA_SIGINFO, accessName): New variables. + * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler. + * If SA_SIGINFO fails, fall back on sa_handler method. + * + * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions. + * (concatenate): Remove. + * + * (runv): Work around bad_wait_if_SIGCHLD_ignored bug. + * Remove reference to OPEN_O_WORK. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Specify subprocess input via file descriptor, not file name. + * Avoid messing with I/O buffers in the child process. + * Define dup in terms of F_DUPFD if it exists. + * Move setmtime to rcsedit.c. Remove lint. + * + * Revision 5.16 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. + * + * Revision 5.15 1993/11/03 17:42:27 eggert + * Use psiginfo and setreuid if available. Move date2str to maketime.c. + * + * Revision 5.14 1992/07/28 16:12:44 eggert + * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug. + * Add mmap_signal, which minimizes signal handling for non-mmap hosts. + * + * Revision 5.13 1992/02/17 23:02:28 eggert + * Work around NFS mmap SIGBUS problem. Add -T support. + * + * Revision 5.12 1992/01/24 18:44:19 eggert + * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint + * + * Revision 5.11 1992/01/06 02:42:34 eggert + * O_BINARY -> OPEN_O_WORK + * while (E) ; -> while (E) continue; + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Support piece tables even if !has_mmap. + * + * Revision 5.9 1991/08/19 03:13:55 eggert + * Add spawn() support. Explicate assumptions about getting invoker's name. + * Standardize user-visible dates. Tune. + * + * Revision 5.8 1991/04/21 11:58:30 eggert + * Plug setuid security hole. + * + * Revision 5.6 1991/02/26 17:48:39 eggert + * Fix setuid bug. Use fread, fwrite more portably. + * Support waitpid. Don't assume -1 is acceptable to W* macros. + * strsave -> str_save (DG/UX name clash) + * + * Revision 5.5 1990/12/04 05:18:49 eggert + * Don't output a blank line after a signal diagnostic. + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:53 eggert + * Remove unneeded setid check. Add awrite(), fremember(). + * + * Revision 5.3 1990/10/06 00:16:45 eggert + * Don't fread F if feof(F). + * + * Revision 5.2 1990/09/04 08:02:31 eggert + * Store fread()'s result in an fread_type object. + * + * Revision 5.1 1990/08/29 07:14:07 eggert + * Declare getpwuid() more carefully. + * + * Revision 5.0 1990/08/22 08:13:46 eggert + * Add setuid support. Permit multiple locks per user. + * Remove compile-time limits; use malloc instead. + * Switch to GMT. Permit dates past 1999/12/31. + * Add -V. Remove snooping. Ansify and Posixate. + * Tune. Some USG hosts define NSIG but not sys_siglist. + * Don't run /bin/sh if it's hopeless. + * Don't leave garbage behind if the output is an empty pipe. + * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup. + * + * Revision 4.6 89/05/01 15:13:40 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/11/08 16:01:02 narten + * corrected use of varargs routines + * + * Revision 4.4 88/08/09 19:13:24 eggert + * Check for memory exhaustion. + * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch. + * Use execv(), not system(); yield exit status like diff(1)'s. + * + * Revision 4.3 87/10/18 10:40:22 narten + * Updating version numbers. Changes relative to 1.1 actually + * relative to 4.1 + * + * Revision 1.3 87/09/24 14:01:01 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:43 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 15:53:13 wft + * Added getcaller() and findlock(). + * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal + * (needed for background jobs in older shells). Added restoreints(). + * Removed printing of full RCS path from logcommand(). + * + * Revision 3.8 83/02/15 15:41:49 wft + * Added routine fastcopy() to copy remainder of a file in blocks. + * + * Revision 3.7 82/12/24 15:25:19 wft + * added catchints(), ignoreints() for catching and ingnoring interrupts; + * fixed catchsig(). + * + * Revision 3.6 82/12/08 21:52:05 wft + * Using DATEFORM to format dates. + * + * Revision 3.5 82/12/04 18:20:49 wft + * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update + * lockedby-field. + * + * Revision 3.4 82/12/03 17:17:43 wft + * Added check to addlock() ensuring only one lock per person. + * Addlock also returns a pointer to the lock created. Deleted fancydate(). + * + * Revision 3.3 82/11/27 12:24:37 wft + * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c. + * Introduced macro SNOOP so that snoop can be placed in directory other than + * TARGETDIR. Changed %02d to %.2d for compatibility reasons. + * + * Revision 3.2 82/10/18 21:15:11 wft + * added function getfullRCSname(). + * + * Revision 3.1 82/10/13 16:17:37 wft + * Cleanup message is now suppressed in quiet mode. + */ + + + + +#include "rcsbase.h" + +libId(utilId, "$FreeBSD$") + +#if !has_memcmp + int +memcmp(s1, s2, n) + void const *s1, *s2; + size_t n; +{ + register unsigned char const + *p1 = (unsigned char const*)s1, + *p2 = (unsigned char const*)s2; + register size_t i = n; + register int r = 0; + while (i-- && !(r = (*p1++ - *p2++))) + ; + return r; +} +#endif + +#if !has_memcpy + void * +memcpy(s1, s2, n) + void *s1; + void const *s2; + size_t n; +{ + register char *p1 = (char*)s1; + register char const *p2 = (char const*)s2; + while (n--) + *p1++ = *p2++; + return s1; +} +#endif + +#if RCS_lint + malloc_type lintalloc; +#endif + +/* + * list of blocks allocated with ftestalloc() + * These blocks can be freed by ffree when we're done with the current file. + * We could put the free block inside struct alloclist, rather than a pointer + * to the free block, but that would be less portable. + */ +struct alloclist { + malloc_type alloc; + struct alloclist *nextalloc; +}; +static struct alloclist *alloced; + + + static malloc_type okalloc P((malloc_type)); + static malloc_type +okalloc(p) + malloc_type p; +{ + if (!p) + faterror("out of memory"); + return p; +} + + malloc_type +testalloc(size) + size_t size; +/* Allocate a block, testing that the allocation succeeded. */ +{ + return okalloc(malloc(size)); +} + + malloc_type +testrealloc(ptr, size) + malloc_type ptr; + size_t size; +/* Reallocate a block, testing that the allocation succeeded. */ +{ + return okalloc(realloc(ptr, size)); +} + + malloc_type +fremember(ptr) + malloc_type ptr; +/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */ +{ + register struct alloclist *q = talloc(struct alloclist); + q->nextalloc = alloced; + alloced = q; + return q->alloc = ptr; +} + + malloc_type +ftestalloc(size) + size_t size; +/* Allocate a block, putting it in 'alloced' so it can be freed later. */ +{ + return fremember(testalloc(size)); +} + + void +ffree() +/* Free all blocks allocated with ftestalloc(). */ +{ + register struct alloclist *p, *q; + for (p = alloced; p; p = q) { + q = p->nextalloc; + tfree(p->alloc); + tfree(p); + } + alloced = 0; +} + + void +ffree1(f) + register char const *f; +/* Free the block f, which was allocated by ftestalloc. */ +{ + register struct alloclist *p, **a = &alloced; + + while ((p = *a)->alloc != f) + a = &p->nextalloc; + *a = p->nextalloc; + tfree(p->alloc); + tfree(p); +} + + char * +str_save(s) + char const *s; +/* Save s in permanently allocated storage. */ +{ + return strcpy(tnalloc(char, strlen(s)+1), s); +} + + char * +fstr_save(s) + char const *s; +/* Save s in storage that will be deallocated when we're done with this file. */ +{ + return strcpy(ftnalloc(char, strlen(s)+1), s); +} + + char * +cgetenv(name) + char const *name; +/* Like getenv(), but yield a copy; getenv() can overwrite old results. */ +{ + register char *p; + + return (p=getenv(name)) ? str_save(p) : p; +} + + char const * +getusername(suspicious) + int suspicious; +/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */ +{ + static char *name; + + if (!name) { + if ( + /* Prefer getenv() unless suspicious; it's much faster. */ +# if getlogin_is_secure + (suspicious + || ( + !(name = cgetenv("LOGNAME")) + && !(name = cgetenv("USER")) + )) + && !(name = getlogin()) +# else + suspicious + || ( + !(name = cgetenv("LOGNAME")) + && !(name = cgetenv("USER")) + && !(name = getlogin()) + ) +# endif + ) { +#if has_getuid && has_getpwuid + struct passwd const *pw = getpwuid(ruid()); + if (!pw) + faterror("no password entry for userid %lu", + (unsigned long)ruid() + ); + name = pw->pw_name; +#else +#if has_setuid + faterror("setuid not supported"); +#else + faterror("Who are you? Please setenv LOGNAME."); +#endif +#endif + } + checksid(name); + } + return name; +} + + + + +#if has_signal + +/* + * Signal handling + * + * Standard C places too many restrictions on signal handlers. + * We obey as many of them as we can. + * Posix places fewer restrictions, and we are Posix-compatible here. + */ + +static sig_atomic_t volatile heldsignal, holdlevel; +#ifdef SA_SIGINFO + static int unsupported_SA_SIGINFO; + static siginfo_t bufsiginfo; + static siginfo_t *volatile heldsiginfo; +#endif + + +#if has_NFS && has_mmap && large_memory && mmap_signal + static char const *accessName; + + void + readAccessFilenameBuffer(filename, p) + char const *filename; + unsigned char const *p; + { + unsigned char volatile t; + accessName = filename; + t = *p; + accessName = 0; + } +#else +# define accessName ((char const *) 0) +#endif + + +#if !has_psignal + +# define psignal my_psignal + static void my_psignal P((int,char const*)); + static void +my_psignal(sig, s) + int sig; + char const *s; +{ + char const *sname = "Unknown signal"; +# if has_sys_siglist && defined(NSIG) + if ((unsigned)sig < NSIG) + sname = sys_siglist[sig]; +# else + switch (sig) { +# ifdef SIGHUP + case SIGHUP: sname = "Hangup"; break; +# endif +# ifdef SIGINT + case SIGINT: sname = "Interrupt"; break; +# endif +# ifdef SIGPIPE + case SIGPIPE: sname = "Broken pipe"; break; +# endif +# ifdef SIGQUIT + case SIGQUIT: sname = "Quit"; break; +# endif +# ifdef SIGTERM + case SIGTERM: sname = "Terminated"; break; +# endif +# ifdef SIGXCPU + case SIGXCPU: sname = "Cputime limit exceeded"; break; +# endif +# ifdef SIGXFSZ + case SIGXFSZ: sname = "Filesize limit exceeded"; break; +# endif +# if has_mmap && large_memory +# if defined(SIGBUS) && mmap_signal==SIGBUS + case SIGBUS: sname = "Bus error"; break; +# endif +# if defined(SIGSEGV) && mmap_signal==SIGSEGV + case SIGSEGV: sname = "Segmentation fault"; break; +# endif +# endif + } +# endif + + /* Avoid calling sprintf etc., in case they're not reentrant. */ + { + char const *p; + char buf[BUFSIZ], *b = buf; + for (p = s; *p; *b++ = *p++) + continue; + *b++ = ':'; + *b++ = ' '; + for (p = sname; *p; *b++ = *p++) + continue; + *b++ = '\n'; + VOID write(STDERR_FILENO, buf, b - buf); + } +} +#endif + +static signal_type catchsig P((int)); +#ifdef SA_SIGINFO + static signal_type catchsigaction P((int,siginfo_t*,void*)); +#endif + + static signal_type +catchsig(s) + int s; +#ifdef SA_SIGINFO +{ + catchsigaction(s, (siginfo_t *)0, (void *)0); +} + static signal_type +catchsigaction(s, i, c) + int s; + siginfo_t *i; + void *c; +#endif +{ +# if sig_zaps_handler + /* If a signal arrives before we reset the handler, we lose. */ + VOID signal(s, SIG_IGN); +# endif + +# ifdef SA_SIGINFO + if (!unsupported_SA_SIGINFO) + i = 0; +# endif + + if (holdlevel) { + heldsignal = s; +# ifdef SA_SIGINFO + if (i) { + bufsiginfo = *i; + heldsiginfo = &bufsiginfo; + } +# endif + return; + } + + ignoreints(); + setrid(); + if (!quietflag) { + /* Avoid calling sprintf etc., in case they're not reentrant. */ + char const *p; + char buf[BUFSIZ], *b = buf; + + if ( ! ( +# if has_mmap && large_memory && mmap_signal + /* Check whether this signal was planned. */ + s == mmap_signal && accessName +# else + 0 +# endif + )) { + char const *nRCS = "\nRCS"; +# if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal + if (s == mmap_signal && i && i->si_errno) { + errno = i->si_errno; + perror(nRCS++); + } +# endif +# if defined(SA_SIGINFO) && has_psiginfo + if (i) + psiginfo(i, nRCS); + else + psignal(s, nRCS); +# else + psignal(s, nRCS); +# endif + } + + for (p = "RCS: "; *p; *b++ = *p++) + continue; +# if has_mmap && large_memory && mmap_signal + if (s == mmap_signal) { + p = accessName; + if (!p) + p = "Was a file changed by some other process? "; + else { + char const *p1; + for (p1 = p; *p1; p1++) + continue; + VOID write(STDERR_FILENO, buf, b - buf); + VOID write(STDERR_FILENO, p, p1 - p); + b = buf; + p = ": Permission denied. "; + } + while (*p) + *b++ = *p++; + } +# endif + for (p = "Cleaning up.\n"; *p; *b++ = *p++) + continue; + VOID write(STDERR_FILENO, buf, b - buf); + } + exiterr(); +} + + void +ignoreints() +{ + ++holdlevel; +} + + void +restoreints() +{ + if (!--holdlevel && heldsignal) +# ifdef SA_SIGINFO + VOID catchsigaction(heldsignal, heldsiginfo, (void *)0); +# else + VOID catchsig(heldsignal); +# endif +} + + +static void setup_catchsig P((int const*,int)); + +#if has_sigaction + + static void check_sig P((int)); + static void + check_sig(r) + int r; + { + if (r != 0) + efaterror("signal handling"); + } + + static void + setup_catchsig(sig, sigs) + int const *sig; + int sigs; + { + register int i, j; + struct sigaction act; + + for (i=sigs; 0<=--i; ) { + check_sig(sigaction(sig[i], (struct sigaction*)0, &act)); + if (act.sa_handler != SIG_IGN) { + act.sa_handler = catchsig; +# ifdef SA_SIGINFO + if (!unsupported_SA_SIGINFO) { +# if has_sa_sigaction + act.sa_sigaction = catchsigaction; +# else + act.sa_handler = catchsigaction; +# endif + act.sa_flags |= SA_SIGINFO; + } +# endif + for (j=sigs; 0<=--j; ) + check_sig(sigaddset(&act.sa_mask, sig[j])); + if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) { +# if defined(SA_SIGINFO) && defined(ENOTSUP) + if (errno == ENOTSUP && !unsupported_SA_SIGINFO) { + /* Turn off use of SA_SIGINFO and try again. */ + unsupported_SA_SIGINFO = 1; + i++; + continue; + } +# endif + check_sig(-1); + } + } + } + } + +#else +#if has_sigblock + + static void + setup_catchsig(sig, sigs) + int const *sig; + int sigs; + { + register int i; + int mask; + + mask = 0; + for (i=sigs; 0<=--i; ) + mask |= sigmask(sig[i]); + mask = sigblock(mask); + for (i=sigs; 0<=--i; ) + if ( + signal(sig[i], catchsig) == SIG_IGN && + signal(sig[i], SIG_IGN) != catchsig + ) + faterror("signal catcher failure"); + VOID sigsetmask(mask); + } + +#else + + static void + setup_catchsig(sig, sigs) + int const *sig; + int sigs; + { + register i; + + for (i=sigs; 0<=--i; ) + if ( + signal(sig[i], SIG_IGN) != SIG_IGN && + signal(sig[i], catchsig) != SIG_IGN + ) + faterror("signal catcher failure"); + } + +#endif +#endif + + +static int const regsigs[] = { +# ifdef SIGHUP + SIGHUP, +# endif +# ifdef SIGINT + SIGINT, +# endif +# ifdef SIGPIPE + SIGPIPE, +# endif +# ifdef SIGQUIT + SIGQUIT, +# endif +# ifdef SIGTERM + SIGTERM, +# endif +# ifdef SIGXCPU + SIGXCPU, +# endif +# ifdef SIGXFSZ + SIGXFSZ, +# endif +}; + + void +catchints() +{ + static int catching_ints; + if (!catching_ints) { + catching_ints = true; + setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs))); + } +} + +#if has_mmap && large_memory && mmap_signal + + /* + * If you mmap an NFS file, and someone on another client removes the last + * link to that file, and you later reference an uncached part of that file, + * you'll get a SIGBUS or SIGSEGV (depending on the operating system). + * Catch the signal and report the problem to the user. + * Unfortunately, there's no portable way to differentiate between this + * problem and actual bugs in the program. + * This NFS problem is rare, thank goodness. + * + * This can also occur if someone truncates the file, even without NFS. + */ + + static int const mmapsigs[] = { mmap_signal }; + + void + catchmmapints() + { + static int catching_mmap_ints; + if (!catching_mmap_ints) { + catching_mmap_ints = true; + setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs))); + } + } +#endif + +#endif /* has_signal */ + + + void +fastcopy(inf,outf) + register RILE *inf; + FILE *outf; +/* Function: copies the remainder of file inf to outf. + */ +{ +#if large_memory +# if maps_memory + awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf); + inf->ptr = inf->lim; +# else + for (;;) { + awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf); + inf->ptr = inf->readlim; + if (inf->ptr == inf->lim) + break; + VOID Igetmore(inf); + } +# endif +#else + char buf[BUFSIZ*8]; + register fread_type rcount; + + /*now read the rest of the file in blocks*/ + while (!feof(inf)) { + if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) { + testIerror(inf); + return; + } + awrite(buf, (size_t)rcount, outf); + } +#endif +} + +#ifndef SSIZE_MAX + /* This does not work in #ifs, but it's good enough for us. */ + /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */ +# define SSIZE_MAX ((unsigned)-1 >> 1) +#endif + + void +awrite(buf, chars, f) + char const *buf; + size_t chars; + FILE *f; +{ + /* Posix 1003.1-1990 ssize_t hack */ + while (SSIZE_MAX < chars) { + if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX) + Oerror(); + buf += SSIZE_MAX; + chars -= SSIZE_MAX; + } + + if (Fwrite(buf, sizeof(*buf), chars, f) != chars) + Oerror(); +} + +/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */ + static int dupSafer P((int)); + static int +dupSafer(fd) + int fd; +{ +# ifdef F_DUPFD + return fcntl(fd, F_DUPFD, STDERR_FILENO + 1); +# else + int e, f, i, used = 0; + while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO) + used |= 1<string); + bufrealloc(b, bl + sl + 4); + p = b->string + bl; + *p++ = c; + *p++ = '\''; + while (*s) { + if (*s == '\'') { + *p++ = '\''; + *p++ = '\\'; + *p++ = '\''; + } + *p++ = *s++; + } + *p++ = '\''; + *p = 0; +} + +#endif + +#if !has_spawn && has_fork +/* +* Output the string S to stderr, without touching any I/O buffers. +* This is useful if you are a child process, whose buffers are usually wrong. +* Exit immediately if the write does not completely succeed. +*/ +static void write_stderr P((char const *)); + static void +write_stderr(s) + char const *s; +{ + size_t slen = strlen(s); + if (write(STDERR_FILENO, s, slen) != slen) + _exit(EXIT_TROUBLE); +} +#endif + +/* +* Run a command. +* infd, if not -1, is the input file descriptor. +* outname, if nonzero, is the name of the output file. +* args[1..] form the command to be run; args[0] might be modified. +*/ + int +runv(infd, outname, args) + int infd; + char const *outname, **args; +{ + int wstatus; + +#if bad_wait_if_SIGCHLD_ignored + static int fixed_SIGCHLD; + if (!fixed_SIGCHLD) { + fixed_SIGCHLD = true; +# ifndef SIGCHLD +# define SIGCHLD SIGCLD +# endif + VOID signal(SIGCHLD, SIG_DFL); + } +#endif + + oflush(); + eflush(); + { +#if has_spawn + int in, out; + char const *file; + + in = -1; + if (infd != -1 && infd != STDIN_FILENO) { + if ((in = dup(STDIN_FILENO)) < 0) { + if (errno != EBADF) + efaterror("spawn input setup"); + in = -2; + } else { +# ifdef F_DUPFD + if (close(STDIN_FILENO) != 0) + efaterror("spawn input close"); +# endif + } + if ( +# ifdef F_DUPFD + fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO +# else + dup2(infd, STDIN_FILENO) != STDIN_FILENO +# endif + ) + efaterror("spawn input redirection"); + } + + out = -1; + if (outname) { + if ((out = dup(STDOUT_FILENO)) < 0) { + if (errno != EBADF) + efaterror("spawn output setup"); + out = -2; + } + if (fdreopen( + STDOUT_FILENO, outname, + O_CREAT | O_TRUNC | O_WRONLY + ) < 0) + efaterror(outname); + } + + wstatus = spawn_RCS(0, args[1], (char**)(args + 1)); +# ifdef RCS_SHELL + if (wstatus == -1 && errno == ENOEXEC) { + args[0] = RCS_SHELL; + wstatus = spawnv(0, args[0], (char**)args); + } +# endif + redirect(in, STDIN_FILENO); + redirect(out, STDOUT_FILENO); +#else +#if has_fork + pid_t pid; + if (!(pid = vfork())) { + char const *notfound; + if (infd != -1 && infd != STDIN_FILENO && ( +# ifdef F_DUPFD + (VOID close(STDIN_FILENO), + fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO) +# else + dup2(infd, STDIN_FILENO) != STDIN_FILENO +# endif + )) { + /* Avoid perror since it may misuse buffers. */ + write_stderr(args[1]); + write_stderr(": I/O redirection failed\n"); + _exit(EXIT_TROUBLE); + } + + if (outname) + if (fdreopen( + STDOUT_FILENO, outname, + O_CREAT | O_TRUNC | O_WRONLY + ) < 0) { + /* Avoid perror since it may misuse buffers. */ + write_stderr(args[1]); + write_stderr(": "); + write_stderr(outname); + write_stderr(": cannot create\n"); + _exit(EXIT_TROUBLE); + } + VOID exec_RCS(args[1], (char**)(args + 1)); + notfound = args[1]; +# ifdef RCS_SHELL + if (errno == ENOEXEC) { + args[0] = notfound = RCS_SHELL; + VOID execv(args[0], (char**)args); + } +# endif + + /* Avoid perror since it may misuse buffers. */ + write_stderr(notfound); + write_stderr(": not found\n"); + _exit(EXIT_TROUBLE); + } + if (pid < 0) + efaterror("fork"); +# if has_waitpid + if (waitpid(pid, &wstatus, 0) < 0) + efaterror("waitpid"); +# else + { + pid_t w; + do { + if ((w = wait(&wstatus)) < 0) + efaterror("wait"); + } while (w != pid); + } +# endif +#else + static struct buf b; + char const *p; + + /* Use system(). On many hosts system() discards signals. Yuck! */ + p = args + 1; + bufscpy(&b, *p); + while (*++p) + bufargcat(&b, ' ', *p); + if (infd != -1 && infd != STDIN_FILENO) { + char redirection[32]; + VOID sprintf(redirection, "<&%d", infd); + bufscat(&b, redirection); + } + if (outname) + bufargcat(&b, '>', outname); + wstatus = system(b.string); +#endif +#endif + } + if (!WIFEXITED(wstatus)) { + if (WIFSIGNALED(wstatus)) { + psignal(WTERMSIG(wstatus), args[1]); + fatcleanup(1); + } + faterror("%s failed for unknown reason", args[1]); + } + return WEXITSTATUS(wstatus); +} + +#define CARGSMAX 20 +/* +* Run a command. +* infd, if not -1, is the input file descriptor. +* outname, if nonzero, is the name of the output file. +* The remaining arguments specify the command and its arguments. +*/ + int +#if has_prototypes +run(int infd, char const *outname, ...) +#else + /*VARARGS2*/ +run(infd, outname, va_alist) + int infd; + char const *outname; + va_dcl +#endif +{ + va_list ap; + char const *rgargs[CARGSMAX]; + register int i; + vararg_start(ap, outname); + for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); ) + if (CARGSMAX <= i) + faterror("too many command arguments"); + va_end(ap); + return runv(infd, outname, rgargs); +} + + +int RCSversion; + + void +setRCSversion(str) + char const *str; +{ + static int oldversion; + + register char const *s = str + 2; + + if (*s) { + int v = VERSION_DEFAULT; + + if (oldversion) + redefined('V'); + oldversion = true; + v = 0; + while (isdigit(*s)) + v = 10*v + *s++ - '0'; + if (*s) + error("%s isn't a number", str); + else if (v < VERSION_min || VERSION_max < v) + error("%s out of range %d..%d", + str, VERSION_min, VERSION_max + ); + + RCSversion = VERSION(v); + } else { + printf("RCS version %s\n", RCS_version_string); + exit(0); + } +} + + int +getRCSINIT(argc, argv, newargv) + int argc; + char **argv, ***newargv; +{ + register char *p, *q, **pp; + char const *ev; + size_t n; + + if ((ev = cgetenv("RCSLOCALID"))) + setRCSLocalId(ev); + + if ((ev = cgetenv("RCSINCEXC"))) + setIncExc(ev); + + if (!(q = cgetenv("RCSINIT"))) + *newargv = argv; + else { + n = argc + 2; + /* + * Count spaces in RCSINIT to allocate a new arg vector. + * This is an upper bound, but it's OK even if too large. + */ + for (p = q; ; ) { + switch (*p++) { + default: + continue; + + case ' ': + case '\b': case '\f': case '\n': + case '\r': case '\t': case '\v': + n++; + continue; + + case '\0': + break; + } + break; + } + *newargv = pp = tnalloc(char*, n); + *pp++ = *argv++; /* copy program name */ + for (p = q; ; ) { + for (;;) { + switch (*q) { + case '\0': + goto copyrest; + + case ' ': + case '\b': case '\f': case '\n': + case '\r': case '\t': case '\v': + q++; + continue; + } + break; + } + *pp++ = p; + ++argc; + for (;;) { + switch ((*p++ = *q++)) { + case '\0': + goto copyrest; + + case '\\': + if (!*q) + goto copyrest; + p[-1] = *q++; + continue; + + default: + continue; + + case ' ': + case '\b': case '\f': case '\n': + case '\r': case '\t': case '\v': + break; + } + break; + } + p[-1] = '\0'; + } + copyrest: + while ((*pp++ = *argv++)) + continue; + } + return argc; +} + + +#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i + +#if has_getuid + uid_t ruid() { cacheid(getuid()); } +#endif +#if has_setuid + uid_t euid() { cacheid(geteuid()); } +#endif + + +#if has_setuid + +/* + * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(), + * because it lets us switch back and forth between arbitrary users. + * If seteuid() doesn't work, we fall back on setuid(), + * which works if saved setuid is supported, + * unless the real or effective user is root. + * This area is such a mess that we always check switches at runtime. + */ + + static void +#if has_prototypes +set_uid_to(uid_t u) +#else + set_uid_to(u) uid_t u; +#endif +/* Become user u. */ +{ + static int looping; + + if (euid() == ruid()) + return; +#if (has_fork||has_spawn) && DIFF_ABSOLUTE +# if has_setreuid + if (setreuid(u==euid() ? ruid() : euid(), u) != 0) + efaterror("setuid"); +# else + if (seteuid(u) != 0) + efaterror("setuid"); +# endif +#endif + if (geteuid() != u) { + if (looping) + return; + looping = true; + faterror("root setuid not supported" + (u?5:0)); + } +} + +static int stick_with_euid; + + void +/* Ignore all calls to seteid() and setrid(). */ +nosetid() +{ + stick_with_euid = true; +} + + void +seteid() +/* Become effective user. */ +{ + if (!stick_with_euid) + set_uid_to(euid()); +} + + void +setrid() +/* Become real user. */ +{ + if (!stick_with_euid) + set_uid_to(ruid()); +} +#endif + + time_t +now() +{ + static time_t t; + if (!t && time(&t) == -1) + efaterror("time"); + return t; +} diff --git a/gnu/usr.bin/rcs/lib/version.c b/gnu/usr.bin/rcs/lib/version.c new file mode 100644 index 00000000000..81f5585b9d1 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/version.c @@ -0,0 +1,2 @@ +#include "rcsbase.h" +char const RCS_version_string[] = "5.7"; diff --git a/gnu/usr.bin/rcs/merge/Makefile b/gnu/usr.bin/rcs/merge/Makefile new file mode 100644 index 00000000000..9022bc4bf6e --- /dev/null +++ b/gnu/usr.bin/rcs/merge/Makefile @@ -0,0 +1,8 @@ +PROG= merge +SRCS= merge.c +CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/merge/merge.1 b/gnu/usr.bin/rcs/merge/merge.1 new file mode 100644 index 00000000000..a4fd35b7986 --- /dev/null +++ b/gnu/usr.bin/rcs/merge/merge.1 @@ -0,0 +1,137 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds r \&\s-1RCS\s0 +.TH MERGE 1 \*(Dt GNU +.SH NAME +merge \- three-way file merge +.SH SYNOPSIS +.B merge +[ +.I "options" +] +.I "file1 file2 file3" +.SH DESCRIPTION +.B merge +incorporates all changes that lead from +.I file2 +to +.I file3 +into +.IR file1 . +The result ordinarily goes into +.IR file1 . +.B merge +is useful for combining separate changes to an original. Suppose +.I file2 +is the original, and both +.I file1 +and +.I file3 +are modifications of +.IR file2 . +Then +.B merge +combines both changes. +.PP +A conflict occurs if both +.I file1 +and +.I file3 +have changes in a common segment of lines. +If a conflict is found, +.B merge +normally outputs a warning and brackets the conflict with +.B <<<<<<< +and +.B >>>>>>> +lines. +A typical conflict will look like this: +.LP +.RS +.nf +.BI <<<<<<< " file A" +.I "lines in file A" +.B "=======" +.I "lines in file B" +.BI >>>>>>> " file B" +.RE +.fi +.LP +If there are conflicts, the user should edit the result and delete one of the +alternatives. +.SH OPTIONS +.TP +.B \-A +Output conflicts using the +.B \-A +style of +.BR diff3 (1), +if supported by +.BR diff3 . +This merges all changes leading from +.I file2 +to +.I file3 +into +.IR file1 , +and generates the most verbose output. +.TP +\f3\-E\fP, \f3\-e\fP +These options specify conflict styles that generate less information +than +.BR \-A . +See +.BR diff3 (1) +for details. +The default is +.BR \-E . +With +.BR \-e , +.B merge +does not warn about conflicts. +.TP +.BI \-L " label" +This option may be given up to three times, and specifies labels +to be used in place of the corresponding file names in conflict reports. +That is, +.B "merge\ \-L\ x\ \-L\ y\ \-L\ z\ a\ b\ c" +generates output that looks like it came from files +.BR x , +.B y +and +.B z +instead of from files +.BR a , +.B b +and +.BR c . +.TP +.BI \-p +Send results to standard output instead of overwriting +.IR file1 . +.TP +.BI \-q +Quiet; do not warn about conflicts. +.TP +.BI \-V +Print \*r's version number. +.SH DIAGNOSTICS +Exit status is 0 for no conflicts, 1 for some conflicts, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH SEE ALSO +diff3(1), diff(1), rcsmerge(1), co(1). +.SH BUGS +It normally does not make sense to merge binary files as if they were text, but +.B merge +tries to do it anyway. +.br diff --git a/gnu/usr.bin/rcs/merge/merge.c b/gnu/usr.bin/rcs/merge/merge.c new file mode 100644 index 00000000000..aa127bf1c8e --- /dev/null +++ b/gnu/usr.bin/rcs/merge/merge.c @@ -0,0 +1,113 @@ +/* merge - three-way file merge */ + +/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +static void badoption P((char const*)); + +static char const usage[] = + "\nmerge: usage: merge [-AeEpqxX3] [-L lab [-L lab [-L lab]]] file1 file2 file3"; + + static void +badoption(a) + char const *a; +{ + error("unknown option: %s%s", a, usage); +} + + +mainProg(mergeId, "merge", "$FreeBSD$") +{ + register char const *a; + char const *arg[3], *label[3], *edarg = 0; + int labels, tostdout; + + labels = 0; + tostdout = false; + + for (; (a = *++argv) && *a++ == '-'; --argc) { + switch (*a++) { + case 'A': case 'E': case 'e': + if (edarg && edarg[1] != (*argv)[1]) + error("%s and %s are incompatible", + edarg, *argv + ); + edarg = *argv; + break; + + case 'p': tostdout = true; break; + case 'q': quietflag = true; break; + + case 'L': + if (3 <= labels) + faterror("too many -L options"); + if (!(label[labels++] = *++argv)) + faterror("-L needs following argument"); + --argc; + break; + + case 'V': + printf("RCS version %s\n", RCS_version_string); + exitmain(0); + + default: + badoption(a - 2); + continue; + } + if (*a) + badoption(a - 2); + } + + if (argc != 4) + faterror("%s arguments%s", + argc<4 ? "not enough" : "too many", usage + ); + + /* This copy keeps us `const'-clean. */ + arg[0] = argv[0]; + arg[1] = argv[1]; + arg[2] = argv[2]; + + for (; labels < 3; labels++) + label[labels] = arg[labels]; + + if (nerror) + exiterr(); + exitmain(merge(tostdout, edarg, label, arg)); +} + + +#if RCS_lint +# define exiterr mergeExit +#endif + void +exiterr() +{ + tempunlink(); + _exit(DIFF_TROUBLE); +} diff --git a/gnu/usr.bin/rcs/rcs/Makefile b/gnu/usr.bin/rcs/rcs/Makefile new file mode 100644 index 00000000000..aa7cc5fb315 --- /dev/null +++ b/gnu/usr.bin/rcs/rcs/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +PROG= rcs +MAN= rcs.1 rcsintro.1 rcsfile.5 +CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/rcs/rcs.1 b/gnu/usr.bin/rcs/rcs/rcs.1 new file mode 100644 index 00000000000..37fb59fea06 --- /dev/null +++ b/gnu/usr.bin/rcs/rcs/rcs.1 @@ -0,0 +1,454 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.if !\n(.g \{\ +. if !\w|\*(lq| \{\ +. ds lq `` +. if \w'\(lq' .ds lq "\(lq +. \} +. if !\w|\*(rq| \{\ +. ds rq '' +. if \w'\(rq' .ds rq "\(rq +. \} +.\} +.TH RCS 1 \*(Dt GNU +.SH NAME +rcs \- change RCS file attributes +.SH SYNOPSIS +.B rcs +.IR "options file " .\|.\|. +.SH DESCRIPTION +.B rcs +creates new \*r files or changes attributes of existing ones. +An \*r file contains multiple revisions of text, +an access list, a change log, +descriptive text, +and some control attributes. +For +.B rcs +to work, the caller's login name must be on the access list, +except if the access list is empty, the caller is the owner of the file +or the superuser, or +the +.B \-i +option is present. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +Revision numbers use the syntax described in +.BR ci (1). +.SH OPTIONS +.TP +.B \-i +Create and initialize a new \*r file, but do not deposit any revision. +If the \*r file has no path prefix, try to place it +first into the subdirectory +.BR ./RCS , +and then into the current directory. +If the \*r file +already exists, print an error message. +.TP +.BI \-a "logins" +Append the login names appearing in the comma-separated list +.I logins +to the access list of the \*r file. +.TP +.BI \-A "oldfile" +Append the access list of +.I oldfile +to the access list of the \*r file. +.TP +.BR \-e [\f2logins\fP] +Erase the login names appearing in the comma-separated list +.I logins +from the access list of the \*r file. +If +.I logins +is omitted, erase the entire access list. +.TP +.BR \-b [\f2rev\fP] +Set the default branch to +.IR rev . +If +.I rev +is omitted, the default +branch is reset to the (dynamically) highest branch on the trunk. +.TP +.BI \-c string +Set the comment leader to +.IR string . +An initial +.BR ci , +or an +.B "rcs\ \-i" +without +.BR \-c , +guesses the comment leader from the suffix of the working filename. +.RS +.PP +This option is obsolescent, since \*r normally uses the preceding +.B $\&Log$ +line's prefix when inserting log lines during checkout (see +.BR co (1)). +However, older versions of \*r use the comment leader instead of the +.B $\&Log$ +line's prefix, so +if you plan to access a file with both old and new versions of \*r, +make sure its comment leader matches its +.B $\&Log$ +line prefix. +.RE +.TP +.BI \-k subst +Set the default keyword substitution to +.IR subst . +The effect of keyword substitution is described in +.BR co (1). +Giving an explicit +.B \-k +option to +.BR co , +.BR rcsdiff , +and +.B rcsmerge +overrides this default. +Beware +.BR "rcs\ \-kv", +because +.B \-kv +is incompatible with +.BR "co\ \-l". +Use +.B "rcs\ \-kkv" +to restore the normal default keyword substitution. +.TP +.BR \-l [\f2rev\fP] +Lock the revision with number +.IR rev . +If a branch is given, lock the latest revision on that branch. +If +.I rev +is omitted, lock the latest revision on the default branch. +Locking prevents overlapping changes. +If someone else already holds the lock, the lock is broken as with +.B "rcs\ \-u" +(see below). +.TP +.BR \-u [\f2rev\fP] +Unlock the revision with number +.IR rev . +If a branch is given, unlock the latest revision on that branch. +If +.I rev +is omitted, remove the latest lock held by the caller. +Normally, only the locker of a revision can unlock it. +Somebody else unlocking a revision breaks the lock. +This causes a mail message to be sent to the original locker. +The message contains a commentary solicited from the breaker. +The commentary is terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +.TP +.B \-L +Set locking to +.IR strict . +Strict locking means that the owner +of an \*r file is not exempt from locking for checkin. +This option should be used for files that are shared. +.TP +.B \-U +Set locking to non-strict. Non-strict locking means that the owner of +a file need not lock a revision for checkin. +This option should +.I not +be used for files that are shared. +Whether default locking is strict is determined by your system administrator, +but it is normally strict. +.TP +\f3\-m\fP\f2rev\fP\f3:\fP\f2msg\fP +Replace revision +.IR rev 's +log message with +.IR msg . +.TP +.B \-M +Do not send mail when breaking somebody else's lock. +This option is not meant for casual use; +it is meant for programs that warn users by other means, and invoke +.B "rcs\ \-u" +only as a low-level lock-breaking operation. +.TP +\f3\-n\fP\f2name\fP[\f3:\fP[\f2rev\fP]] +Associate the symbolic name +.I name +with the branch or +revision +.IR rev . +Delete the symbolic name if both +.B : +and +.I rev +are omitted; otherwise, print an error message if +.I name +is already associated with +another number. +If +.I rev +is symbolic, it is expanded before association. +A +.I rev +consisting of a branch number followed by a +.B .\& +stands for the current latest revision in the branch. +A +.B : +with an empty +.I rev +stands for the current latest revision on the default branch, +normally the trunk. +For example, +.BI "rcs\ \-n" name ":\ RCS/*" +associates +.I name +with the current latest revision of all the named \*r files; +this contrasts with +.BI "rcs\ \-n" name ":$\ RCS/*" +which associates +.I name +with the revision numbers extracted from keyword strings +in the corresponding working files. +.TP +\f3\-N\fP\f2name\fP[\f3:\fP[\f2rev\fP]] +Act like +.BR \-n , +except override any previous assignment of +.IR name . +.TP +.BI \-o range +deletes (\*(lqoutdates\*(rq) the revisions given by +.IR range . +A range consisting of a single revision number means that revision. +A range consisting of a branch number means the latest revision on that +branch. +A range of the form +.IB rev1 : rev2 +means +revisions +.I rev1 +to +.I rev2 +on the same branch, +.BI : rev +means from the beginning of the branch containing +.I rev +up to and including +.IR rev , +and +.IB rev : +means +from revision +.I rev +to the end of the branch containing +.IR rev . +None of the outdated revisions can have branches or locks. +.TP +.B \-q +Run quietly; do not print diagnostics. +.TP +.B \-I +Run interactively, even if the standard input is not a terminal. +.TP +.B \-s\f2state\fP\f1[\fP:\f2rev\fP\f1]\fP +Set the state attribute of the revision +.I rev +to +.IR state . +If +.I rev +is a branch number, assume the latest revision on that branch. +If +.I rev +is omitted, assume the latest revision on the default branch. +Any identifier is acceptable for +.IR state . +A useful set of states +is +.B Exp +(for experimental), +.B Stab +(for stable), and +.B Rel +(for +released). +By default, +.BR ci (1) +sets the state of a revision to +.BR Exp . +.TP +.BR \-t [\f2file\fP] +Write descriptive text from the contents of the named +.I file +into the \*r file, deleting the existing text. +The +.IR file +pathname cannot begin with +.BR \- . +If +.I file +is omitted, obtain the text from standard input, +terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +Prompt for the text if interaction is possible; see +.BR \-I . +With +.BR \-i , +descriptive text is obtained +even if +.B \-t +is not given. +.TP +.BI \-t\- string +Write descriptive text from the +.I string +into the \*r file, deleting the existing text. +.TP +.B \-T +Preserve the modification time on the \*r file +unless a revision is removed. +This option can suppress extensive recompilation caused by a +.BR make (1) +dependency of some copy of the working file on the \*r file. +Use this option with care; it can suppress recompilation even when it is needed, +i.e. when a change to the \*r file +would mean a change to keyword strings in the working file. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.TP +.BI \-z zone +Use +.I zone +as the default time zone. +This option has no effect; +it is present for compatibility with other \*r commands. +.PP +At least one explicit option must be given, +to ensure compatibility with future planned extensions +to the +.B rcs +command. +.SH COMPATIBILITY +The +.BI \-b rev +option generates an \*r file that cannot be parsed by \*r version 3 or earlier. +.PP +The +.BI \-k subst +options (except +.BR \-kkv ) +generate an \*r file that cannot be parsed by \*r version 4 or earlier. +.PP +Use +.BI "rcs \-V" n +to make an \*r file acceptable to \*r version +.I n +by discarding information that would confuse version +.IR n . +.PP +\*r version 5.5 and earlier does not support the +.B \-x +option, and requires a +.B ,v +suffix on an \*r pathname. +.SH FILES +.B rcs +accesses files much as +.BR ci (1) +does, +except that it uses the effective user for all accesses, +it does not write the working file or its directory, +and it does not even read the working file unless a revision number of +.B $ +is specified. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +The \*r pathname and the revisions outdated are written to +the diagnostic output. +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +rcsintro(1), co(1), ci(1), ident(1), rcsclean(1), rcsdiff(1), +rcsmerge(1), rlog(1), rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH BUGS +A catastrophe (e.g. a system crash) can cause \*r to leave behind +a semaphore file that causes later invocations of \*r to claim +that the \*r file is in use. +To fix this, remove the semaphore file. +A semaphore file's name typically begins with +.B , +or ends with +.BR _ . +.PP +The separator for revision ranges in the +.B \-o +option used to be +.B \- +instead of +.BR : , +but this leads to confusion when symbolic names contain +.BR \- . +For backwards compatibility +.B "rcs \-o" +still supports the old +.B \- +separator, but it warns about this obsolete use. +.PP +Symbolic names need not refer to existing revisions or branches. +For example, the +.B \-o +option does not remove symbolic names for the outdated revisions; you must use +.B \-n +to remove the names. +.br diff --git a/gnu/usr.bin/rcs/rcs/rcs.c b/gnu/usr.bin/rcs/rcs/rcs.c new file mode 100644 index 00000000000..dfb622a6f80 --- /dev/null +++ b/gnu/usr.bin/rcs/rcs/rcs.c @@ -0,0 +1,1629 @@ +/* Change RCS file attributes. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.21 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.20 1995/06/01 16:23:43 eggert + * (main): Warn if no options were given. Punctuate messages properly. + * + * (sendmail): Rewind mailmess before flushing it. + * Output another warning if mail should work but fails. + * + * (buildeltatext): Pass "--binary" if -kb and if --binary makes a difference. + * + * Revision 5.19 1994/03/17 14:05:48 eggert + * Use ORCSerror to clean up after a fatal error. Remove lint. + * Specify subprocess input via file descriptor, not file name. Remove lint. + * Flush stderr after prompt. + * + * Revision 5.18 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.17 1993/11/03 17:42:27 eggert + * Add -z. Don't lose track of -m or -t when there are no other changes. + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.16 1992/07/28 16:12:44 eggert + * rcs -l now asks whether you want to break the lock. + * Add -V. Set RCS file's mode and time at right moment. + * + * Revision 5.15 1992/02/17 23:02:20 eggert + * Add -T. + * + * Revision 5.14 1992/01/27 16:42:53 eggert + * Add -M. Avoid invoking umask(); it's one less thing to configure. + * Add support for bad_creat0. lint -> RCS_lint + * + * Revision 5.13 1992/01/06 02:42:34 eggert + * Avoid changing RCS file in common cases where no change can occur. + * + * Revision 5.12 1991/11/20 17:58:08 eggert + * Don't read the delta tree from a nonexistent RCS file. + * + * Revision 5.11 1991/10/07 17:32:46 eggert + * Remove lint. + * + * Revision 5.10 1991/08/19 23:17:54 eggert + * Add -m, -r$, piece tables. Revision separator is `:', not `-'. Tune. + * + * Revision 5.9 1991/04/21 11:58:18 eggert + * Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.8 1991/02/25 07:12:38 eggert + * strsave -> str_save (DG/UX name clash) + * 0444 -> S_IRUSR|S_IRGRP|S_IROTH for portability + * + * Revision 5.7 1990/12/18 17:19:21 eggert + * Fix bug with multiple -n and -N options. + * + * Revision 5.6 1990/12/04 05:18:40 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.5 1990/11/11 00:06:35 eggert + * Fix `rcs -e' core dump. + * + * Revision 5.4 1990/11/01 05:03:33 eggert + * Add -I and new -t behavior. Permit arbitrary data in logs. + * + * Revision 5.3 1990/10/04 06:30:16 eggert + * Accumulate exit status across files. + * + * Revision 5.2 1990/09/04 08:02:17 eggert + * Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:13:51 eggert + * Remove unused setuid support. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:42 eggert + * Don't lose names when applying -a option to multiple files. + * Remove compile-time limits; use malloc instead. Add setuid support. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. Add -V. Fix umask bug. Make linting easier. Tune. + * Yield proper exit status. Check diff's output. + * + * Revision 4.11 89/05/01 15:12:06 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.10 88/11/08 16:01:54 narten + * didn't install previous patch correctly + * + * Revision 4.9 88/11/08 13:56:01 narten + * removed include (not needed) + * minor fix for -A option + * + * Revision 4.8 88/08/09 19:12:27 eggert + * Don't access freed storage. + * Use execv(), not system(); yield proper exit status; remove lint. + * + * Revision 4.7 87/12/18 11:37:17 narten + * lint cleanups (Guy Harris) + * + * Revision 4.6 87/10/18 10:28:48 narten + * Updating verison numbers. Changes relative to 1.1 are actually + * relative to 4.3 + * + * Revision 1.4 87/09/24 13:58:52 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.3 87/03/27 14:21:55 jenkins + * Port to suns + * + * Revision 1.2 85/12/17 13:59:09 albitz + * Changed setstate to rcs_setstate because of conflict with random.o. + * + * Revision 4.3 83/12/15 12:27:33 wft + * rcs -u now breaks most recent lock if it can't find a lock by the caller. + * + * Revision 4.2 83/12/05 10:18:20 wft + * Added conditional compilation for sending mail. + * Alternatives: V4_2BSD, V6, USG, and other. + * + * Revision 4.1 83/05/10 16:43:02 wft + * Simplified breaklock(); added calls to findlock() and getcaller(). + * Added option -b (default branch). Updated -s and -w for -b. + * Removed calls to stat(); now done by pairfilenames(). + * Replaced most catchints() calls with restoreints(). + * Removed check for exit status of delivermail(). + * Directed all interactive output to stderr. + * + * Revision 3.9.1.1 83/12/02 22:08:51 wft + * Added conditional compilation for 4.2 sendmail and 4.1 delivermail. + * + * Revision 3.9 83/02/15 15:38:39 wft + * Added call to fastcopy() to copy remainder of RCS file. + * + * Revision 3.8 83/01/18 17:37:51 wft + * Changed sendmail(): now uses delivermail, and asks whether to break the lock. + * + * Revision 3.7 83/01/15 18:04:25 wft + * Removed putree(); replaced with puttree() in rcssyn.c. + * Combined putdellog() and scanlogtext(); deleted putdellog(). + * Cleaned up diagnostics and error messages. Fixed problem with + * mutilated files in case of deletions in 2 files in a single command. + * Changed marking of selector from 'D' to DELETE. + * + * Revision 3.6 83/01/14 15:37:31 wft + * Added ignoring of interrupts while new RCS file is renamed; + * Avoids deletion of RCS files by interrupts. + * + * Revision 3.5 82/12/10 21:11:39 wft + * Removed unused variables, fixed checking of return code from diff, + * introduced variant COMPAT2 for skipping Suffix on -A files. + * + * Revision 3.4 82/12/04 13:18:20 wft + * Replaced getdelta() with gettree(), changed breaklock to update + * field lockedby, added some diagnostics. + * + * Revision 3.3 82/12/03 17:08:04 wft + * Replaced getlogin() with getpwuid(), flcose() with ffclose(), + * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x). + * fixed -u for missing revno. Disambiguated structure members. + * + * Revision 3.2 82/10/18 21:05:07 wft + * rcs -i now generates a file mode given by the umask minus write permission; + * otherwise, rcs keeps the mode, but removes write permission. + * I added a check for write error, fixed call to getlogin(), replaced + * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed + * conflicting, long identifiers. + * + * Revision 3.1 82/10/13 16:11:07 wft + * fixed type of variables receiving from getc() (char -> int). + */ + + +#include "rcsbase.h" + +struct Lockrev { + char const *revno; + struct Lockrev * nextrev; +}; + +struct Symrev { + char const *revno; + char const *ssymbol; + int override; + struct Symrev * nextsym; +}; + +struct Message { + char const *revno; + struct cbuf message; + struct Message *nextmessage; +}; + +struct Status { + char const *revno; + char const *status; + struct Status * nextstatus; +}; + +enum changeaccess {append, erase}; +struct chaccess { + char const *login; + enum changeaccess command; + struct chaccess *nextchaccess; +}; + +struct delrevpair { + char const *strt; + char const *end; + int code; +}; + +static int branchpoint P((struct hshentry*,struct hshentry*)); +static int breaklock P((struct hshentry const*)); +static int buildeltatext P((struct hshentries const*)); +static int doaccess P((void)); +static int doassoc P((void)); +static int dolocks P((void)); +static int domessages P((void)); +static int rcs_setstate P((char const*,char const*)); +static int removerevs P((void)); +static int sendmail P((char const*,char const*)); +static int setlock P((char const*)); +static struct Lockrev **rmnewlocklst P((char const*)); +static struct hshentry *searchcutpt P((char const*,int,struct hshentries*)); +static void buildtree P((void)); +static void cleanup P((void)); +static void getaccessor P((char*,enum changeaccess)); +static void getassoclst P((int,char*)); +static void getchaccess P((char const*,enum changeaccess)); +static void getdelrev P((char*)); +static void getmessage P((char*)); +static void getstates P((char*)); +static void scanlogtext P((struct hshentry*,int)); + +static struct buf numrev; +static char const *headstate; +static int chgheadstate, exitstatus, lockhead, unlockcaller; +static int suppress_mail; +static struct Lockrev *newlocklst, *rmvlocklst; +static struct Message *messagelst, **nextmessage; +static struct Status *statelst, **nextstate; +static struct Symrev *assoclst, **nextassoc; +static struct chaccess *chaccess, **nextchaccess; +static struct delrevpair delrev; +static struct hshentry *cuthead, *cuttail, *delstrt; +static struct hshentries *gendeltas; + +mainProg(rcsId, "rcs", "$FreeBSD$") +{ + static char const cmdusage[] = + "\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iILqTU} -ksubst -mrev:msg -{nN}name[:[rev]] -orange -sstate[:rev] -t[text] -Vn -xsuff -zzone file ..."; + + char *a, **newargv, *textfile; + char const *branchsym, *commsyml; + int branchflag, changed, expmode, initflag; + int strictlock, strict_selected, textflag; + int keepRCStime, Ttimeflag; + size_t commsymlen; + struct buf branchnum; + struct Lockrev *lockpt; + struct Lockrev **curlock, **rmvlock; + struct Status * curstate; + + nosetid(); + + nextassoc = &assoclst; + nextchaccess = &chaccess; + nextmessage = &messagelst; + nextstate = &statelst; + branchsym = commsyml = textfile = 0; + branchflag = strictlock = false; + bufautobegin(&branchnum); + commsymlen = 0; + curlock = &newlocklst; + rmvlock = &rmvlocklst; + expmode = -1; + suffixes = X_DEFAULT; + initflag= textflag = false; + strict_selected = 0; + Ttimeflag = false; + + /* preprocessing command options */ + if (1 < argc && argv[1][0] != '-') + warn("No options were given; this usage is obsolescent."); + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + while (a = *++argv, 0<--argc && *a++=='-') { + switch (*a++) { + + case 'i': /* initial version */ + initflag = true; + break; + + case 'b': /* change default branch */ + if (branchflag) redefined('b'); + branchflag= true; + branchsym = a; + break; + + case 'c': /* change comment symbol */ + if (commsyml) redefined('c'); + commsyml = a; + commsymlen = strlen(a); + break; + + case 'a': /* add new accessor */ + getaccessor(*argv+1, append); + break; + + case 'A': /* append access list according to accessfile */ + if (!*a) { + error("missing pathname after -A"); + break; + } + *argv = a; + if (0 < pairnames(1,argv,rcsreadopen,true,false)) { + while (AccessList) { + getchaccess(str_save(AccessList->login),append); + AccessList = AccessList->nextaccess; + } + Izclose(&finptr); + } + break; + + case 'e': /* remove accessors */ + getaccessor(*argv+1, erase); + break; + + case 'l': /* lock a revision if it is unlocked */ + if (!*a) { + /* Lock head or default branch. */ + lockhead = true; + break; + } + *curlock = lockpt = talloc(struct Lockrev); + lockpt->revno = a; + lockpt->nextrev = 0; + curlock = &lockpt->nextrev; + break; + + case 'u': /* release lock of a locked revision */ + if (!*a) { + unlockcaller=true; + break; + } + *rmvlock = lockpt = talloc(struct Lockrev); + lockpt->revno = a; + lockpt->nextrev = 0; + rmvlock = &lockpt->nextrev; + curlock = rmnewlocklst(lockpt->revno); + break; + + case 'L': /* set strict locking */ + if (strict_selected) { + if (!strictlock) /* Already selected -U? */ + warn("-U overridden by -L"); + } + strictlock = true; + strict_selected = true; + break; + + case 'U': /* release strict locking */ + if (strict_selected) { + if (strictlock) /* Already selected -L? */ + warn("-L overridden by -U"); + } + strict_selected = true; + break; + + case 'n': /* add new association: error, if name exists */ + if (!*a) { + error("missing symbolic name after -n"); + break; + } + getassoclst(false, (*argv)+1); + break; + + case 'N': /* add or change association */ + if (!*a) { + error("missing symbolic name after -N"); + break; + } + getassoclst(true, (*argv)+1); + break; + + case 'm': /* change log message */ + getmessage(a); + break; + + case 'M': /* do not send mail */ + suppress_mail = true; + break; + + case 'o': /* delete revisions */ + if (delrev.strt) redefined('o'); + if (!*a) { + error("missing revision range after -o"); + break; + } + getdelrev( (*argv)+1 ); + break; + + case 's': /* change state attribute of a revision */ + if (!*a) { + error("state missing after -s"); + break; + } + getstates( (*argv)+1); + break; + + case 't': /* change descriptive text */ + textflag=true; + if (*a) { + if (textfile) redefined('t'); + textfile = a; + } + break; + + case 'T': /* do not update last-mod time for minor changes */ + if (*a) + goto unknown; + Ttimeflag = true; + break; + + case 'I': + interactiveflag = true; + break; + + case 'q': + quietflag = true; + break; + + case 'x': + suffixes = a; + break; + + case 'V': + setRCSversion(*argv); + break; + + case 'z': + zone_set(a); + break; + + case 'k': /* set keyword expand mode */ + if (0 <= expmode) redefined('k'); + if (0 <= (expmode = str2expmode(a))) + break; + /* fall into */ + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + }; + } /* end processing of options */ + + /* Now handle all pathnames. */ + if (nerror) cleanup(); + else if (argc < 1) faterror("no input file%s", cmdusage); + else for (; 0 < argc; cleanup(), ++argv, --argc) { + + ffree(); + + if ( initflag ) { + switch (pairnames(argc, argv, rcswriteopen, false, false)) { + case -1: break; /* not exist; ok */ + case 0: continue; /* error */ + case 1: rcserror("already exists"); + continue; + } + } + else { + switch (pairnames(argc, argv, rcswriteopen, true, false)) { + case -1: continue; /* not exist */ + case 0: continue; /* errors */ + case 1: break; /* file exists; ok*/ + } + } + + + /* + * RCSname contains the name of the RCS file, and + * workname contains the name of the working file. + * if !initflag, finptr contains the file descriptor for the + * RCS file. The admin node is initialized. + */ + + diagnose("RCS file: %s\n", RCSname); + + changed = initflag | textflag; + keepRCStime = Ttimeflag; + if (!initflag) { + if (!checkaccesslist()) continue; + gettree(); /* Read the delta tree. */ + } + + /* update admin. node */ + if (strict_selected) { + changed |= StrictLocks ^ strictlock; + StrictLocks = strictlock; + } + if ( + commsyml && + ( + commsymlen != Comment.size || + memcmp(commsyml, Comment.string, commsymlen) != 0 + ) + ) { + Comment.string = commsyml; + Comment.size = strlen(commsyml); + changed = true; + } + if (0 <= expmode && Expand != expmode) { + Expand = expmode; + changed = true; + } + + /* update default branch */ + if (branchflag && expandsym(branchsym, &branchnum)) { + if (countnumflds(branchnum.string)) { + if (cmpnum(Dbranch, branchnum.string) != 0) { + Dbranch = branchnum.string; + changed = true; + } + } else + if (Dbranch) { + Dbranch = 0; + changed = true; + } + } + + changed |= doaccess(); /* Update access list. */ + + changed |= doassoc(); /* Update association list. */ + + changed |= dolocks(); /* Update locks. */ + + changed |= domessages(); /* Update log messages. */ + + /* update state attribution */ + if (chgheadstate) { + /* change state of default branch or head */ + if (!Dbranch) { + if (!Head) + rcswarn("can't change states in an empty tree"); + else if (strcmp(Head->state, headstate) != 0) { + Head->state = headstate; + changed = true; + } + } else + changed |= rcs_setstate(Dbranch,headstate); + } + for (curstate = statelst; curstate; curstate = curstate->nextstatus) + changed |= rcs_setstate(curstate->revno,curstate->status); + + cuthead = cuttail = 0; + if (delrev.strt && removerevs()) { + /* rebuild delta tree if some deltas are deleted */ + if ( cuttail ) + VOID genrevs( + cuttail->num, (char *)0, (char *)0, (char *)0, + &gendeltas + ); + buildtree(); + changed = true; + keepRCStime = false; + } + + if (nerror) + continue; + + putadmin(); + if ( Head ) + puttree(Head, frewrite); + putdesc(textflag,textfile); + + if ( Head) { + if (delrev.strt || messagelst) { + if (!cuttail || buildeltatext(gendeltas)) { + advise_access(finptr, MADV_SEQUENTIAL); + scanlogtext((struct hshentry *)0, false); + /* copy rest of delta text nodes that are not deleted */ + changed = true; + } + } + } + + if (initflag) { + /* Adjust things for donerewrite's sake. */ + if (stat(workname, &RCSstat) != 0) { +# if bad_creat0 + mode_t m = umask(0); + (void) umask(m); + RCSstat.st_mode = (S_IRUSR|S_IRGRP|S_IROTH) & ~m; +# else + changed = -1; +# endif + } + RCSstat.st_nlink = 0; + keepRCStime = false; + } + if (donerewrite(changed, + keepRCStime ? RCSstat.st_mtime : (time_t)-1 + ) != 0) + break; + + diagnose("done\n"); + } + + tempunlink(); + exitmain(exitstatus); +} /* end of main (rcs) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); + Ozclose(&fcopy); + ORCSclose(); + dirtempunlink(); +} + + void +exiterr() +{ + ORCSerror(); + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + + + static void +getassoclst(flag, sp) +int flag; +char * sp; +/* Function: associate a symbolic name to a revision or branch, */ +/* and store in assoclst */ + +{ + struct Symrev * pt; + char const *temp; + int c; + + while ((c = *++sp) == ' ' || c == '\t' || c =='\n') + continue; + temp = sp; + sp = checksym(sp, ':'); /* check for invalid symbolic name */ + c = *sp; *sp = '\0'; + while( c == ' ' || c == '\t' || c == '\n') c = *++sp; + + if ( c != ':' && c != '\0') { + error("invalid string %s after option -n or -N",sp); + return; + } + + pt = talloc(struct Symrev); + pt->ssymbol = temp; + pt->override = flag; + if (c == '\0') /* delete symbol */ + pt->revno = 0; + else { + while ((c = *++sp) == ' ' || c == '\n' || c == '\t') + continue; + pt->revno = sp; + } + pt->nextsym = 0; + *nextassoc = pt; + nextassoc = &pt->nextsym; +} + + + static void +getchaccess(login, command) + char const *login; + enum changeaccess command; +{ + register struct chaccess *pt; + + pt = talloc(struct chaccess); + pt->login = login; + pt->command = command; + pt->nextchaccess = 0; + *nextchaccess = pt; + nextchaccess = &pt->nextchaccess; +} + + + + static void +getaccessor(opt, command) + char *opt; + enum changeaccess command; +/* Function: get the accessor list of options -e and -a, */ +/* and store in chaccess */ + + +{ + register c; + register char *sp; + + sp = opt; + while ((c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') + continue; + if ( c == '\0') { + if (command == erase && sp-opt == 1) { + getchaccess((char*)0, command); + return; + } + error("missing login name after option -a or -e"); + return; + } + + while( c != '\0') { + getchaccess(sp, command); + sp = checkid(sp,','); + c = *sp; *sp = '\0'; + while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp); + } +} + + + static void +getmessage(option) + char *option; +{ + struct Message *pt; + struct cbuf cb; + char *m; + + if (!(m = strchr(option, ':'))) { + error("-m option lacks revision number"); + return; + } + *m++ = 0; + cb = cleanlogmsg(m, strlen(m)); + if (!cb.size) { + error("-m option lacks log message"); + return; + } + pt = talloc(struct Message); + pt->revno = option; + pt->message = cb; + pt->nextmessage = 0; + *nextmessage = pt; + nextmessage = &pt->nextmessage; +} + + + static void +getstates(sp) +char *sp; +/* Function: get one state attribute and the corresponding */ +/* revision and store in statelst */ + +{ + char const *temp; + struct Status *pt; + register c; + + while ((c = *++sp) ==' ' || c == '\t' || c == '\n') + continue; + temp = sp; + sp = checkid(sp,':'); /* check for invalid state attribute */ + c = *sp; *sp = '\0'; + while( c == ' ' || c == '\t' || c == '\n' ) c = *++sp; + + if ( c == '\0' ) { /* change state of def. branch or Head */ + chgheadstate = true; + headstate = temp; + return; + } + else if ( c != ':' ) { + error("missing ':' after state in option -s"); + return; + } + + while ((c = *++sp) == ' ' || c == '\t' || c == '\n') + continue; + pt = talloc(struct Status); + pt->status = temp; + pt->revno = sp; + pt->nextstatus = 0; + *nextstate = pt; + nextstate = &pt->nextstatus; +} + + + + static void +getdelrev(sp) +char *sp; +/* Function: get revision range or branch to be deleted, */ +/* and place in delrev */ +{ + int c; + struct delrevpair *pt; + int separator; + + pt = &delrev; + while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t') + continue; + + /* Support old ambiguous '-' syntax; this will go away. */ + if (strchr(sp,':')) + separator = ':'; + else { + if (strchr(sp,'-') && VERSION(5) <= RCSversion) + warn("`-' is obsolete in `-o%s'; use `:' instead", sp); + separator = '-'; + } + + if (c == separator) { /* -o:rev */ + while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t') + continue; + pt->strt = sp; pt->code = 1; + while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp); + *sp = '\0'; + pt->end = 0; + return; + } + else { + pt->strt = sp; + while( c != ' ' && c != '\n' && c != '\t' && c != '\0' + && c != separator ) c = *++sp; + *sp = '\0'; + while( c == ' ' || c == '\n' || c == '\t' ) c = *++sp; + if ( c == '\0' ) { /* -o rev or branch */ + pt->code = 0; + pt->end = 0; + return; + } + if (c != separator) { + error("invalid range %s %s after -o", pt->strt, sp); + } + while ((c = *++sp) == ' ' || c == '\n' || c == '\t') + continue; + if (!c) { /* -orev: */ + pt->code = 2; + pt->end = 0; + return; + } + } + /* -orev1:rev2 */ + pt->end = sp; pt->code = 3; + while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp; + *sp = '\0'; +} + + + + + static void +scanlogtext(delta,edit) + struct hshentry *delta; + int edit; +/* Function: Scans delta text nodes up to and including the one given + * by delta, or up to last one present, if !delta. + * For the one given by delta (if delta), the log message is saved into + * delta->log if delta==cuttail; the text is edited if EDIT is set, else copied. + * Assumes the initial lexeme must be read in first. + * Does not advance nexttok after it is finished, except if !delta. + */ +{ + struct hshentry const *nextdelta; + struct cbuf cb; + + for (;;) { + foutptr = 0; + if (eoflex()) { + if(delta) + rcsfaterror("can't find delta for revision %s", + delta->num + ); + return; /* no more delta text nodes */ + } + nextlex(); + if (!(nextdelta=getnum())) + fatserror("delta number corrupted"); + if (nextdelta->selector) { + foutptr = frewrite; + aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog); + } + getkeystring(Klog); + if (nextdelta == cuttail) { + cb = savestring(&curlogbuf); + if (!delta->log.string) + delta->log = cleanlogmsg(curlogbuf.string, cb.size); + nextlex(); + delta->igtext = getphrases(Ktext); + } else { + if (nextdelta->log.string && nextdelta->selector) { + foutptr = 0; + readstring(); + foutptr = frewrite; + putstring(foutptr, false, nextdelta->log, true); + afputc(nextc, foutptr); + } else + readstring(); + ignorephrases(Ktext); + } + getkeystring(Ktext); + + if (delta==nextdelta) + break; + readstring(); /* skip over it */ + + } + /* got the one we're looking for */ + if (edit) + editstring((struct hshentry*)0); + else + enterstring(); +} + + + + static struct Lockrev ** +rmnewlocklst(which) + char const *which; +/* Remove lock to revision WHICH from newlocklst. */ +{ + struct Lockrev *pt, **pre; + + pre = &newlocklst; + while ((pt = *pre)) + if (strcmp(pt->revno, which) != 0) + pre = &pt->nextrev; + else { + *pre = pt->nextrev; + tfree(pt); + } + return pre; +} + + + + static int +doaccess() +{ + register struct chaccess *ch; + register struct access **p, *t; + register int changed = false; + + for (ch = chaccess; ch; ch = ch->nextchaccess) { + switch (ch->command) { + case erase: + if (!ch->login) { + if (AccessList) { + AccessList = 0; + changed = true; + } + } else + for (p = &AccessList; (t = *p); p = &t->nextaccess) + if (strcmp(ch->login, t->login) == 0) { + *p = t->nextaccess; + changed = true; + break; + } + break; + case append: + for (p = &AccessList; ; p = &t->nextaccess) + if (!(t = *p)) { + *p = t = ftalloc(struct access); + t->login = ch->login; + t->nextaccess = 0; + changed = true; + break; + } else if (strcmp(ch->login, t->login) == 0) + break; + break; + } + } + return changed; +} + + + static int +sendmail(Delta, who) + char const *Delta, *who; +/* Function: mail to who, informing him that his lock on delta was + * broken by caller. Ask first whether to go ahead. Return false on + * error or if user decides not to break the lock. + */ +{ +#ifdef SENDMAIL + char const *messagefile; + int old1, old2, c, status; + FILE * mailmess; +#endif + + + aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who); + if (suppress_mail) + return true; + if (!yesorno(false, "Do you want to break the lock? [ny](n): ")) + return false; + + /* go ahead with breaking */ +#ifdef SENDMAIL + messagefile = maketemp(0); + if (!(mailmess = fopenSafer(messagefile, "w+"))) { + efaterror(messagefile); + } + + aprintf(mailmess, "Subject: Broken lock on %s\n\nYour lock on revision %s of file %s\nhas been broken by %s for the following reason:\n", + basefilename(RCSname), Delta, getfullRCSname(), getcaller() + ); + aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr); + eflush(); + + old1 = '\n'; old2 = ' '; + for (; ;) { + c = getcstdin(); + if (feof(stdin)) { + aprintf(mailmess, "%c\n", old1); + break; + } + else if ( c == '\n' && old1 == '.' && old2 == '\n') + break; + else { + afputc(old1, mailmess); + old2 = old1; old1 = c; + if (c == '\n') { + aputs(">> ", stderr); + eflush(); + } + } + } + Orewind(mailmess); + aflush(mailmess); + status = run(fileno(mailmess), (char*)0, SENDMAIL, who, (char*)0); + Ozclose(&mailmess); + if (status == 0) + return true; + warn("Mail failed."); +#endif + warn("Mail notification of broken locks is not available."); + warn("Please tell `%s' why you broke the lock.", who); + return(true); +} + + + + static int +breaklock(delta) + struct hshentry const *delta; +/* function: Finds the lock held by caller on delta, + * and removes it. + * Sends mail if a lock different from the caller's is broken. + * Prints an error message if there is no such lock or error. + */ +{ + register struct rcslock *next, **trail; + char const *num; + + num=delta->num; + for (trail = &Locks; (next = *trail); trail = &next->nextlock) + if (strcmp(num, next->delta->num) == 0) { + if ( + strcmp(getcaller(),next->login) != 0 + && !sendmail(num, next->login) + ) { + rcserror("revision %s still locked by %s", + num, next->login + ); + return false; + } + diagnose("%s unlocked\n", next->delta->num); + *trail = next->nextlock; + next->delta->lockedby = 0; + return true; + } + rcserror("no lock set on revision %s", num); + return false; +} + + + + static struct hshentry * +searchcutpt(object, length, store) + char const *object; + int length; + struct hshentries *store; +/* Function: Search store and return entry with number being object. */ +/* cuttail = 0, if the entry is Head; otherwise, cuttail */ +/* is the entry point to the one with number being object */ + +{ + cuthead = 0; + while (compartial(store->first->num, object, length)) { + cuthead = store->first; + store = store->rest; + } + return store->first; +} + + + + static int +branchpoint(strt, tail) +struct hshentry *strt, *tail; +/* Function: check whether the deltas between strt and tail */ +/* are locked or branch point, return 1 if any is */ +/* locked or branch point; otherwise, return 0 and */ +/* mark deleted */ + +{ + struct hshentry *pt; + struct rcslock const *lockpt; + + for (pt = strt; pt != tail; pt = pt->next) { + if ( pt->branches ){ /* a branch point */ + rcserror("can't remove branch point %s", pt->num); + return true; + } + for (lockpt = Locks; lockpt; lockpt = lockpt->nextlock) + if (lockpt->delta == pt) { + rcserror("can't remove locked revision %s", pt->num); + return true; + } + pt->selector = false; + diagnose("deleting revision %s\n",pt->num); + } + return false; +} + + + + static int +removerevs() +/* Function: get the revision range to be removed, and place the */ +/* first revision removed in delstrt, the revision before */ +/* delstrt in cuthead (0, if delstrt is head), and the */ +/* revision after the last removed revision in cuttail (0 */ +/* if the last is a leaf */ + +{ + struct hshentry *target, *target2, *temp; + int length; + int cmp; + + if (!expandsym(delrev.strt, &numrev)) return 0; + target = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas); + if ( ! target ) return 0; + cmp = cmpnum(target->num, numrev.string); + length = countnumflds(numrev.string); + + if (delrev.code == 0) { /* -o rev or -o branch */ + if (length & 1) + temp=searchcutpt(target->num,length+1,gendeltas); + else if (cmp) { + rcserror("Revision %s doesn't exist.", numrev.string); + return 0; + } + else + temp = searchcutpt(numrev.string, length, gendeltas); + cuttail = target->next; + if ( branchpoint(temp, cuttail) ) { + cuttail = 0; + return 0; + } + delstrt = temp; /* first revision to be removed */ + return 1; + } + + if (length & 1) { /* invalid branch after -o */ + rcserror("invalid branch range %s after -o", numrev.string); + return 0; + } + + if (delrev.code == 1) { /* -o -rev */ + if ( length > 2 ) { + temp = searchcutpt( target->num, length-1, gendeltas); + cuttail = target->next; + } + else { + temp = searchcutpt(target->num, length, gendeltas); + cuttail = target; + while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) ) + cuttail = cuttail->next; + } + if ( branchpoint(temp, cuttail) ){ + cuttail = 0; + return 0; + } + delstrt = temp; + return 1; + } + + if (delrev.code == 2) { /* -o rev- */ + if ( length == 2 ) { + temp = searchcutpt(target->num, 1,gendeltas); + if (cmp) + cuttail = target; + else + cuttail = target->next; + } + else { + if (cmp) { + cuthead = target; + if ( !(temp = target->next) ) return 0; + } + else + temp = searchcutpt(target->num, length, gendeltas); + getbranchno(temp->num, &numrev); /* get branch number */ + VOID genrevs(numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas); + } + if ( branchpoint( temp, cuttail ) ) { + cuttail = 0; + return 0; + } + delstrt = temp; + return 1; + } + + /* -o rev1-rev2 */ + if (!expandsym(delrev.end, &numrev)) return 0; + if ( + length != countnumflds(numrev.string) + || (length>2 && compartial(numrev.string, target->num, length-1)) + ) { + rcserror("invalid revision range %s-%s", + target->num, numrev.string + ); + return 0; + } + + target2 = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas); + if ( ! target2 ) return 0; + + if ( length > 2) { /* delete revisions on branches */ + if ( cmpnum(target->num, target2->num) > 0) { + cmp = cmpnum(target2->num, numrev.string); + temp = target; + target = target2; + target2 = temp; + } + if (cmp) { + if ( ! cmpnum(target->num, target2->num) ) { + rcserror("Revisions %s-%s don't exist.", + delrev.strt, delrev.end + ); + return 0; + } + cuthead = target; + temp = target->next; + } + else + temp = searchcutpt(target->num, length, gendeltas); + cuttail = target2->next; + } + else { /* delete revisions on trunk */ + if ( cmpnum( target->num, target2->num) < 0 ) { + temp = target; + target = target2; + target2 = temp; + } + else + cmp = cmpnum(target2->num, numrev.string); + if (cmp) { + if ( ! cmpnum(target->num, target2->num) ) { + rcserror("Revisions %s-%s don't exist.", + delrev.strt, delrev.end + ); + return 0; + } + cuttail = target2; + } + else + cuttail = target2->next; + temp = searchcutpt(target->num, length, gendeltas); + } + if ( branchpoint(temp, cuttail) ) { + cuttail = 0; + return 0; + } + delstrt = temp; + return 1; +} + + + + static int +doassoc() +/* Add or delete (if !revno) association that is stored in assoclst. */ +{ + char const *p; + int changed = false; + struct Symrev const *curassoc; + struct assoc **pre, *pt; + + /* add new associations */ + for (curassoc = assoclst; curassoc; curassoc = curassoc->nextsym) { + char const *ssymbol = curassoc->ssymbol; + + if (!curassoc->revno) { /* delete symbol */ + for (pre = &Symbols; ; pre = &pt->nextassoc) + if (!(pt = *pre)) { + rcswarn("can't delete nonexisting symbol %s", ssymbol); + break; + } else if (strcmp(pt->symbol, ssymbol) == 0) { + *pre = pt->nextassoc; + changed = true; + break; + } + } + else { + if (curassoc->revno[0]) { + p = 0; + if (expandsym(curassoc->revno, &numrev)) + p = fstr_save(numrev.string); + } else if (!(p = tiprev())) + rcserror("no latest revision to associate with symbol %s", + ssymbol + ); + if (p) + changed |= addsymbol(p, ssymbol, curassoc->override); + } + } + return changed; +} + + + + static int +dolocks() +/* Function: remove lock for caller or first lock if unlockcaller is set; + * remove locks which are stored in rmvlocklst, + * add new locks which are stored in newlocklst, + * add lock for Dbranch or Head if lockhead is set. + */ +{ + struct Lockrev const *lockpt; + struct hshentry *target; + int changed = false; + + if (unlockcaller) { /* find lock for caller */ + if ( Head ) { + if (Locks) { + switch (findlock(true, &target)) { + case 0: + /* remove most recent lock */ + changed |= breaklock(Locks->delta); + break; + case 1: + diagnose("%s unlocked\n",target->num); + changed = true; + break; + } + } else { + rcswarn("No locks are set."); + } + } else { + rcswarn("can't unlock an empty tree"); + } + } + + /* remove locks which are stored in rmvlocklst */ + for (lockpt = rmvlocklst; lockpt; lockpt = lockpt->nextrev) + if (expandsym(lockpt->revno, &numrev)) { + target = genrevs(numrev.string, (char *)0, (char *)0, (char *)0, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + rcserror("can't unlock nonexisting revision %s", + lockpt->revno + ); + else + changed |= breaklock(target); + /* breaklock does its own diagnose */ + } + + /* add new locks which stored in newlocklst */ + for (lockpt = newlocklst; lockpt; lockpt = lockpt->nextrev) + changed |= setlock(lockpt->revno); + + if (lockhead) /* lock default branch or head */ + if (Dbranch) + changed |= setlock(Dbranch); + else if (Head) + changed |= setlock(Head->num); + else + rcswarn("can't lock an empty tree"); + return changed; +} + + + + static int +setlock(rev) + char const *rev; +/* Function: Given a revision or branch number, finds the corresponding + * delta and locks it for caller. + */ +{ + struct hshentry *target; + int r; + + if (expandsym(rev, &numrev)) { + target = genrevs(numrev.string, (char*)0, (char*)0, + (char*)0, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + rcserror("can't lock nonexisting revision %s", + numrev.string + ); + else { + if ((r = addlock(target, false)) < 0 && breaklock(target)) + r = addlock(target, true); + if (0 <= r) { + if (r) + diagnose("%s locked\n", target->num); + return r; + } + } + } + return 0; +} + + + static int +domessages() +{ + struct hshentry *target; + struct Message *p; + int changed = false; + + for (p = messagelst; p; p = p->nextmessage) + if ( + expandsym(p->revno, &numrev) && + (target = genrevs( + numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas + )) + ) { + /* + * We can't check the old log -- it's much later in the file. + * We pessimistically assume that it changed. + */ + target->log = p->message; + changed = true; + } + return changed; +} + + + static int +rcs_setstate(rev,status) + char const *rev, *status; +/* Function: Given a revision or branch number, finds the corresponding delta + * and sets its state to status. + */ +{ + struct hshentry *target; + + if (expandsym(rev, &numrev)) { + target = genrevs(numrev.string, (char*)0, (char*)0, + (char*)0, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + rcserror("can't set state of nonexisting revision %s", + numrev.string + ); + else if (strcmp(target->state, status) != 0) { + target->state = status; + return true; + } + } + return false; +} + + + + + + static int +buildeltatext(deltas) + struct hshentries const *deltas; +/* Function: put the delta text on frewrite and make necessary */ +/* change to delta text */ +{ + register FILE *fcut; /* temporary file to rebuild delta tree */ + char const *cutname; + + fcut = 0; + cuttail->selector = false; + scanlogtext(deltas->first, false); + if ( cuthead ) { + cutname = maketemp(3); + if (!(fcut = fopenSafer(cutname, FOPEN_WPLUS_WORK))) { + efaterror(cutname); + } + + while (deltas->first != cuthead) { + deltas = deltas->rest; + scanlogtext(deltas->first, true); + } + + snapshotedit(fcut); + Orewind(fcut); + aflush(fcut); + } + + while (deltas->first != cuttail) + scanlogtext((deltas = deltas->rest)->first, true); + finishedit((struct hshentry*)0, (FILE*)0, true); + Ozclose(&fcopy); + + if (fcut) { + char const *diffname = maketemp(0); + char const *diffv[6 + !!OPEN_O_BINARY]; + char const **diffp = diffv; + *++diffp = DIFF; + *++diffp = DIFFFLAGS; +# if OPEN_O_BINARY + if (Expand == BINARY_EXPAND) + *++diffp == "--binary"; +# endif + *++diffp = "-"; + *++diffp = resultname; + *++diffp = 0; + switch (runv(fileno(fcut), diffname, diffv)) { + case DIFF_FAILURE: case DIFF_SUCCESS: break; + default: rcsfaterror("diff failed"); + } + Ofclose(fcut); + return putdtext(cuttail,diffname,frewrite,true); + } else + return putdtext(cuttail,resultname,frewrite,false); +} + + + + static void +buildtree() +/* Function: actually removes revisions whose selector field */ +/* is false, and rebuilds the linkage of deltas. */ +/* asks for reconfirmation if deleting last revision*/ +{ + struct hshentry * Delta; + struct branchhead *pt, *pre; + + if ( cuthead ) + if ( cuthead->next == delstrt ) + cuthead->next = cuttail; + else { + pre = pt = cuthead->branches; + while( pt && pt->hsh != delstrt ) { + pre = pt; + pt = pt->nextbranch; + } + if ( cuttail ) + pt->hsh = cuttail; + else if ( pt == pre ) + cuthead->branches = pt->nextbranch; + else + pre->nextbranch = pt->nextbranch; + } + else { + if (!cuttail && !quietflag) { + if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) { + rcserror("No revision deleted"); + Delta = delstrt; + while( Delta) { + Delta->selector = true; + Delta = Delta->next; + } + return; + } + } + Head = cuttail; + } + return; +} + +#if RCS_lint +/* This lets us lint everything all at once. */ + +char const cmdid[] = ""; + +#define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();} + + int +main(argc, argv) + int argc; + char **argv; +{ + go(ciId, ciExit); + go(coId, coExit); + go(identId, identExit); + go(mergeId, mergeExit); + go(rcsId, exiterr); + go(rcscleanId, rcscleanExit); + go(rcsdiffId, rdiffExit); + go(rcsmergeId, rmergeExit); + go(rlogId, rlogExit); + return 0; +} +#endif diff --git a/gnu/usr.bin/rcs/rcs/rcsfile.5 b/gnu/usr.bin/rcs/rcs/rcsfile.5 new file mode 100644 index 00000000000..0d8aac874d4 --- /dev/null +++ b/gnu/usr.bin/rcs/rcs/rcsfile.5 @@ -0,0 +1,425 @@ +.lf 1 ./rcsfile.5in +.\" Set p to 1 if your formatter can handle pic output. +.if t .nr p 1 +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSFILE 5 \*(Dt GNU +.SH NAME +rcsfile \- format of RCS file +.SH DESCRIPTION +An \*r file's +contents are described by the grammar +below. +.PP +The text is free format: space, backspace, tab, newline, vertical +tab, form feed, and carriage return (collectively, +.IR "white space") +have no significance except in strings. +However, white space cannot appear within an id, num, or sym, +and an \*r file must end with a newline. +.PP +Strings are enclosed by +.BR @ . +If a string contains a +.BR @ , +it must be doubled; +otherwise, strings can contain arbitrary binary data. +.PP +The meta syntax uses the following conventions: `|' (bar) separates +alternatives; `{' and `}' enclose optional phrases; `{' and `}*' enclose +phrases that can be repeated zero or more times; +`{' and '}+' enclose phrases that must appear at least once and can be +repeated; +Terminal symbols are in +.BR boldface ; +nonterminal symbols are in +.IR italics . +.LP +.nr w \w'\f3deltatext\fP ' +.nr y \w'\f3newphrase\fP ' +.if \nw<\ny .nr w \ny +.nr x \w'\f3branches\fP' +.nr y \w'{ \f3comment\fP' +.if \nx<\ny .nr x \ny +.nr y \w'\f3{ branch\fP' +.if \nx<\ny .nr x \ny +.ta \nwu +\w'::= 'u +\nxu+\w' 'u +.fc # +.nf +\f2rcstext\fP ::= \f2admin\fP {\f2delta\fP}* \f2desc\fP {\f2deltatext\fP}* +.LP +\f2admin\fP ::= \f3head\fP {\f2num\fP}\f3;\fP + { \f3branch\fP {\f2num\fP}\f3;\fP } + \f3access\fP {\f2id\fP}*\f3;\fP + \f3symbols\fP {\f2sym\fP \f3:\fP \f2num\fP}*\f3;\fP + \f3locks\fP {\f2id\fP \f3:\fP \f2num\fP}*\f3;\fP {\f3strict ;\fP} + { \f3comment\fP {\f2string\fP}\f3;\fP } + { \f3expand\fP {\f2string\fP}\f3;\fP } + { \f2newphrase\fP }* +.LP +\f2delta\fP ::= \f2num\fP + \f3date\fP \f2num\fP\f3;\fP + \f3author\fP \f2id\fP\f3;\fP + \f3state\fP {\f2id\fP}\f3;\fP + \f3branches\fP {\f2num\fP}*\f3;\fP + \f3next\fP {\f2num\fP}\f3;\fP + { \f2newphrase\fP }* +.LP +\f2desc\fP ::= \f3desc\fP \f2string\fP +.LP +\f2deltatext\fP ::= \f2num\fP + \f3log\fP \f2string\fP + { \f2newphrase\fP }* + \f3text\fP \f2string\fP +.LP +\f2num\fP ::= {\f2digit\fP | \f3.\fP}+ +.LP +\f2digit\fP ::= \f30\fP | \f31\fP | \f32\fP | \f33\fP | \f34\fP | \f35\fP | \f36\fP | \f37\fP | \f38\fP | \f39\fP +.LP +\f2id\fP ::= {\f2num\fP} \f2idchar\fP {\f2idchar\fP | \f2num\fP}* +.LP +\f2sym\fP ::= {\f2digit\fP}* \f2idchar\fP {\f2idchar\fP | \f2digit\fP}* +.LP +\f2idchar\fP ::= any visible graphic character except \f2special\fP +.LP +\f2special\fP ::= \f3$\fP | \f3,\fP | \f3.\fP | \f3:\fP | \f3;\fP | \f3@\fP +.LP +\f2string\fP ::= \f3@\fP{any character, with \f3@\fP doubled}*\f3@\fP +.LP +\f2newphrase\fP ::= \f2id\fP \f2word\fP* \f3;\fP +.LP +\f2word\fP ::= \f2id\fP | \f2num\fP | \f2string\fP | \f3:\fP +.fi +.PP +Identifiers are case sensitive. Keywords are in lower case only. +The sets of keywords and identifiers can overlap. +In most environments \*r uses the \s-1ISO\s0 8859/1 encoding: +visible graphic characters are codes 041\-176 and 240\-377, +and white space characters are codes 010\-015 and 040. +.PP +Dates, which appear after the +.B date +keyword, are of the form +\f2Y\fP\f3.\fP\f2mm\fP\f3.\fP\f2dd\fP\f3.\fP\f2hh\fP\f3.\fP\f2mm\fP\f3.\fP\f2ss\fP, +where +.I Y +is the year, +.I mm +the month (01\-12), +.I dd +the day (01\-31), +.I hh +the hour (00\-23), +.I mm +the minute (00\-59), +and +.I ss +the second (00\-60). +.I Y +contains just the last two digits of the year +for years from 1900 through 1999, +and all the digits of years thereafter. +Dates use the Gregorian calendar; times use UTC. +.PP +The +.I newphrase +productions in the grammar are reserved for future extensions +to the format of \*r files. +No +.I newphrase +will begin with any keyword already in use. +.PP +The +.I delta +nodes form a tree. All nodes whose numbers +consist of a single pair +(e.g., 2.3, 2.1, 1.3, etc.) +are on the trunk, and are linked through the +.B next +field in order of decreasing numbers. +The +.B head +field in the +.I admin +node points to the head of that sequence (i.e., contains +the highest pair). +The +.B branch +node in the admin node indicates the default +branch (or revision) for most \*r operations. +If empty, the default +branch is the highest branch on the trunk. +.PP +All +.I delta +nodes whose numbers consist of +.RI 2 n +fields +.RI ( n \(>=2) +(e.g., 3.1.1.1, 2.1.2.2, etc.) +are linked as follows. +All nodes whose first +.RI 2 n \-1 +number fields are identical are linked through the +.B next +field in order of increasing numbers. +For each such sequence, +the +.I delta +node whose number is identical to the first +.RI 2 n \-2 +number fields of the deltas on that sequence is called the branchpoint. +The +.B branches +field of a node contains a list of the +numbers of the first nodes of all sequences for which it is a branchpoint. +This list is ordered in increasing numbers. +.LP +The following diagram shows an example of an \*r file's organization. +.if !\np \{\ +.nf +.vs 12 +.ne 36 +.cs 1 20 +.eo + + Head + | + | + v / \ + --------- / \ + / \ / \ | | / \ / \ + / \ / \ | 2.1 | / \ / \ + / \ / \ | | / \ / \ +/1.2.1.3\ /1.3.1.1\ | | /1.2.2.2\ /1.2.2.1.1.1\ +--------- --------- --------- --------- ------------- + ^ ^ | ^ ^ + | | | | | + | | v | | + / \ | --------- / \ | + / \ | \ 1.3 / / \ | + / \ ---------\ / / \----------- +/1.2.1.1\ \ / /1.2.2.1\ +--------- \ / --------- + ^ | ^ + | | | + | v | + | --------- | + | \ 1.2 / | + ----------------------\ /--------- + \ / + \ / + | + | + v + --------- + \ 1.1 / + \ / + \ / + \ / + +.ec +.cs 1 +.vs +.fi +.\} +.if \np \{\ +.lf 232 +.PS 4.250i 3.812i +.\" -2.0625 -4.25 1.75 0 +.\" 0.000i 4.250i 3.812i 0.000i +.nr 00 \n(.u +.nf +.nr 0x 1 +\h'3.812i' +.sp -1 +.lf 242 +\h'2.062i-(\w'Head'u/2u)'\v'0.125i-(0v/2u)+0v+0.22m'Head +.sp -1 +\h'2.062i'\v'0.250i'\D'l0.000i 0.500i' +.sp -1 +\h'2.087i'\v'0.650i'\D'l-0.025i 0.100i' +.sp -1 +\h'2.062i'\v'0.750i'\D'l-0.025i -0.100i' +.sp -1 +\h'1.688i'\v'1.250i'\D'l0.750i 0.000i' +.sp -1 +\h'2.438i'\v'1.250i'\D'l0.000i -0.500i' +.sp -1 +\h'2.438i'\v'0.750i'\D'l-0.750i 0.000i' +.sp -1 +\h'1.688i'\v'0.750i'\D'l0.000i 0.500i' +.sp -1 +.lf 244 +\h'2.062i-(\w'2.1'u/2u)'\v'1.000i-(0v/2u)+0v+0.22m'2.1 +.sp -1 +\h'2.062i'\v'1.250i'\D'l0.000i 0.500i' +.sp -1 +\h'2.087i'\v'1.650i'\D'l-0.025i 0.100i' +.sp -1 +\h'2.062i'\v'1.750i'\D'l-0.025i -0.100i' +.sp -1 +.lf 246 +\h'2.062i-(\w'1.3'u/2u)'\v'2.000i-(1v/2u)+0v+0.22m'1.3 +.sp -1 +\h'2.062i'\v'2.250i'\D'l-0.375i -0.500i' +.sp -1 +\h'1.688i'\v'1.750i'\D'l0.750i 0.000i' +.sp -1 +\h'2.438i'\v'1.750i'\D'l-0.375i 0.500i' +.sp -1 +\h'1.875i'\v'2.000i'\D'~-0.500i 0.000i 0.000i -0.500i' +.sp -1 +\h'1.350i'\v'1.600i'\D'l0.025i -0.100i' +.sp -1 +\h'1.375i'\v'1.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 249 +\h'1.375i-(\w'1.3.1.1'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.3.1.1 +.sp -1 +\h'1.375i'\v'1.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'1.000i'\v'1.500i'\D'l0.750i 0.000i' +.sp -1 +\h'1.750i'\v'1.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.062i'\v'2.250i'\D'l0.000i 0.500i' +.sp -1 +\h'2.087i'\v'2.650i'\D'l-0.025i 0.100i' +.sp -1 +\h'2.062i'\v'2.750i'\D'l-0.025i -0.100i' +.sp -1 +.lf 252 +\h'2.062i-(\w'1.2'u/2u)'\v'3.000i-(1v/2u)+0v+0.22m'1.2 +.sp -1 +\h'2.062i'\v'3.250i'\D'l-0.375i -0.500i' +.sp -1 +\h'1.688i'\v'2.750i'\D'l0.750i 0.000i' +.sp -1 +\h'2.438i'\v'2.750i'\D'l-0.375i 0.500i' +.sp -1 +\h'1.875i'\v'3.000i'\D'~-0.500i 0.000i -0.500i 0.000i -0.500i 0.000i 0.000i -0.500i' +.sp -1 +\h'0.350i'\v'2.600i'\D'l0.025i -0.100i' +.sp -1 +\h'0.375i'\v'2.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 255 +\h'0.375i-(\w'1.2.1.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.1.1 +.sp -1 +\h'0.375i'\v'2.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'0.000i'\v'2.500i'\D'l0.750i 0.000i' +.sp -1 +\h'0.750i'\v'2.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'0.375i'\v'2.000i'\D'l0.000i -0.500i' +.sp -1 +\h'0.350i'\v'1.600i'\D'l0.025i -0.100i' +.sp -1 +\h'0.375i'\v'1.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 257 +\h'0.375i-(\w'1.2.1.3'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.1.3 +.sp -1 +\h'0.375i'\v'1.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'0.000i'\v'1.500i'\D'l0.750i 0.000i' +.sp -1 +\h'0.750i'\v'1.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.250i'\v'3.000i'\D'~0.500i 0.000i 0.000i -0.500i' +.sp -1 +\h'2.725i'\v'2.600i'\D'l0.025i -0.100i' +.sp -1 +\h'2.750i'\v'2.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 261 +\h'2.750i-(\w'1.2.2.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.2.1 +.sp -1 +\h'2.750i'\v'2.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'2.375i'\v'2.500i'\D'l0.750i 0.000i' +.sp -1 +\h'3.125i'\v'2.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.938i'\v'2.250i'\D'~0.500i 0.000i 0.000i -0.500i 0.000i -0.500i' +.sp -1 +\h'3.413i'\v'1.350i'\D'l0.025i -0.100i' +.sp -1 +\h'3.438i'\v'1.250i'\D'l0.025i 0.100i' +.sp -1 +.lf 264 +\h'3.438i-(\w'\s-21.2.2.1.1.1\s0'u/2u)'\v'1.000i-(1v/2u)+1v+0.22m'\s-21.2.2.1.1.1\s0 +.sp -1 +\h'3.438i'\v'0.750i'\D'l-0.375i 0.500i' +.sp -1 +\h'3.062i'\v'1.250i'\D'l0.750i 0.000i' +.sp -1 +\h'3.812i'\v'1.250i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.750i'\v'2.000i'\D'l0.000i -0.500i' +.sp -1 +\h'2.725i'\v'1.600i'\D'l0.025i -0.100i' +.sp -1 +\h'2.750i'\v'1.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 267 +\h'2.750i-(\w'1.2.2.2'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.2.2 +.sp -1 +\h'2.750i'\v'1.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'2.375i'\v'1.500i'\D'l0.750i 0.000i' +.sp -1 +\h'3.125i'\v'1.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.062i'\v'3.250i'\D'l0.000i 0.500i' +.sp -1 +\h'2.087i'\v'3.650i'\D'l-0.025i 0.100i' +.sp -1 +\h'2.062i'\v'3.750i'\D'l-0.025i -0.100i' +.sp -1 +.lf 270 +\h'2.062i-(\w'1.1'u/2u)'\v'4.000i-(1v/2u)+0v+0.22m'1.1 +.sp -1 +\h'2.062i'\v'4.250i'\D'l-0.375i -0.500i' +.sp -1 +\h'1.688i'\v'3.750i'\D'l0.750i 0.000i' +.sp -1 +\h'2.438i'\v'3.750i'\D'l-0.375i 0.500i' +.sp -1 +.sp 4.250i+1 +.if \n(00 .fi +.br +.nr 0x 0 +.lf 271 +.PE +.lf 272 +.\} +.SH IDENTIFICATION +.de VL +\\$2 +.. +Author: Walter F. Tichy, +Purdue University, West Lafayette, IN, 47907. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH SEE ALSO +rcsintro(1), ci(1), co(1), ident(1), rcs(1), rcsclean(1), rcsdiff(1), +rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/gnu/usr.bin/rcs/rcs/rcsintro.1 b/gnu/usr.bin/rcs/rcs/rcsintro.1 new file mode 100644 index 00000000000..edcd9ee9389 --- /dev/null +++ b/gnu/usr.bin/rcs/rcs/rcsintro.1 @@ -0,0 +1,302 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.if !\n(.g \{\ +. if !\w|\*(lq| \{\ +. ds lq `` +. if \w'\(lq' .ds lq "\(lq +. \} +. if !\w|\*(rq| \{\ +. ds rq '' +. if \w'\(rq' .ds rq "\(rq +. \} +.\} +.am SS +.LP +.. +.TH RCSINTRO 1 \*(Dt GNU +.SH NAME +rcsintro \- introduction to RCS commands +.SH DESCRIPTION +The Revision Control System (\*r) manages multiple revisions of files. +\*r automates the storing, retrieval, logging, identification, and merging +of revisions. \*r is useful for text that is revised frequently, for example +programs, documentation, graphics, papers, and form letters. +.PP +The basic user interface is extremely simple. The novice only needs +to learn two commands: +.BR ci (1) +and +.BR co (1). +.BR ci , +short for \*(lqcheck in\*(rq, deposits the contents of a +file into an archival file called an \*r file. An \*r file +contains all revisions of a particular file. +.BR co , +short for \*(lqcheck out\*(rq, retrieves revisions from an \*r file. +.SS "Functions of \*r" +.IP \(bu +Store and retrieve multiple revisions of text. \*r saves all old +revisions in a space efficient way. +Changes no longer destroy the original, because the +previous revisions remain accessible. Revisions can be retrieved according to +ranges of revision numbers, symbolic names, dates, authors, and +states. +.IP \(bu +Maintain a complete history of changes. +\*r logs all changes automatically. +Besides the text of each revision, \*r stores the author, the date and time of +check-in, and a log message summarizing the change. +The logging makes it easy to find out +what happened to a module, without having to compare +source listings or having to track down colleagues. +.IP \(bu +Resolve access conflicts. When two or more programmers wish to +modify the same revision, \*r alerts the programmers and prevents one +modification from corrupting the other. +.IP \(bu +Maintain a tree of revisions. \*r can maintain separate lines of development +for each module. It stores a tree structure that represents the +ancestral relationships among revisions. +.IP \(bu +Merge revisions and resolve conflicts. +Two separate lines of development of a module can be coalesced by merging. +If the revisions to be merged affect the same sections of code, \*r alerts the +user about the overlapping changes. +.IP \(bu +Control releases and configurations. +Revisions can be assigned symbolic names +and marked as released, stable, experimental, etc. +With these facilities, configurations of modules can be +described simply and directly. +.IP \(bu +Automatically identify each revision with name, revision number, +creation time, author, etc. +The identification is like a stamp that can be embedded at an appropriate place +in the text of a revision. +The identification makes it simple to determine which +revisions of which modules make up a given configuration. +.IP \(bu +Minimize secondary storage. \*r needs little extra space for +the revisions (only the differences). If intermediate revisions are +deleted, the corresponding deltas are compressed accordingly. +.SS "Getting Started with \*r" +Suppose you have a file +.B f.c +that you wish to put under control of \*r. +If you have not already done so, make an \*r directory with the command +.IP +.B "mkdir RCS" +.LP +Then invoke the check-in command +.IP +.B "ci f.c" +.LP +This command creates an \*r file in the +.B RCS +directory, +stores +.B f.c +into it as revision 1.1, and +deletes +.BR f.c . +It also asks you for a description. The description +should be a synopsis of the contents of the file. All later check-in +commands will ask you for a log entry, which should summarize the +changes that you made. +.PP +Files in the \*r directory are called \*r files; +the others are called working files. +To get back the working file +.B f.c +in the previous example, use the check-out +command +.IP +.B "co f.c" +.LP +This command extracts the latest revision from the \*r file +and writes +it into +.BR f.c . +If you want to edit +.BR f.c , +you must lock it as you check it out with the command +.IP +.B "co \-l f.c" +.LP +You can now edit +.BR f.c . +.PP +Suppose after some editing you want to know what changes that you have made. +The command +.IP +.B "rcsdiff f.c" +.LP +tells you the difference between the most recently checked-in version +and the working file. +You can check the file back in by invoking +.IP +.B "ci f.c" +.LP +This increments the revision number properly. +.PP +If +.B ci +complains with the message +.IP +.BI "ci error: no lock set by " "your name" +.LP +then you have tried to check in a file even though you did not +lock it when you checked it out. +Of course, it is too late now to do the check-out with locking, because +another check-out would +overwrite your modifications. Instead, invoke +.IP +.B "rcs \-l f.c" +.LP +This command will lock the latest revision for you, unless somebody +else got ahead of you already. In this case, you'll have to negotiate with +that person. +.PP +Locking assures that you, and only you, can check in the next update, and +avoids nasty problems if several people work on the same file. +Even if a revision is locked, it can still be checked out for +reading, compiling, etc. All that locking +prevents is a +.I "check-in" +by anybody but the locker. +.PP +If your \*r file is private, i.e., if you are the only person who is going +to deposit revisions into it, strict locking is not needed and you +can turn it off. +If strict locking is turned off, +the owner of the \*r file need not have a lock for check-in; all others +still do. Turning strict locking off and on is done with the commands +.IP +.BR "rcs \-U f.c" " and " "rcs \-L f.c" +.LP +If you don't want to clutter your working directory with \*r files, create +a subdirectory called +.B RCS +in your working directory, and move all your \*r +files there. \*r commands will look first into that directory to find +needed files. All the commands discussed above will still work, without any +modification. +(Actually, pairs of \*r and working files can be specified in three ways: +(a) both are given, (b) only the working file is given, (c) only the +\*r file is given. Both \*r and working files may have arbitrary path prefixes; +\*r commands pair them up intelligently.) +.PP +To avoid the deletion of the working file during check-in (in case you want to +continue editing or compiling), invoke +.IP +.BR "ci \-l f.c" " or " "ci \-u f.c" +.LP +These commands check in +.B f.c +as usual, but perform an implicit +check-out. The first form also locks the checked in revision, the second one +doesn't. Thus, these options save you one check-out operation. +The first form is useful if you want to continue editing, +the second one if you just want to read the file. +Both update the identification markers in your working file (see below). +.PP +You can give +.B ci +the number you want assigned to a checked in +revision. Assume all your revisions were numbered 1.1, 1.2, 1.3, etc., +and you would like to start release 2. +The command +.IP +.BR "ci \-r2 f.c" " or " "ci \-r2.1 f.c" +.LP +assigns the number 2.1 to the new revision. +From then on, +.B ci +will number the subsequent revisions +with 2.2, 2.3, etc. The corresponding +.B co +commands +.IP +.BR "co \-r2 f.c" " and " "co \-r2.1 f.c" +.PP +retrieve the latest revision numbered +.RI 2. x +and the revision 2.1, +respectively. +.B co +without a revision number selects +the latest revision on the +.IR trunk , +i.e. the highest +revision with a number consisting of two fields. Numbers with more than two +fields are needed for branches. +For example, to start a branch at revision 1.3, invoke +.IP +.B "ci \-r1.3.1 f.c" +.LP +This command starts a branch numbered 1 at revision 1.3, and assigns +the number 1.3.1.1 to the new revision. For more information about +branches, see +.BR rcsfile (5). +.SS "Automatic Identification" +\*r can put special strings for identification into your source and object +code. To obtain such identification, place the marker +.IP +.B "$\&Id$" +.LP +into your text, for instance inside a comment. +\*r will replace this marker with a string of the form +.IP +.BI $\&Id: " filename revision date time author state " $ +.LP +With such a marker on the first page of each module, you can +always see with which revision you are working. +\*r keeps the markers up to date automatically. +To propagate the markers into your object code, simply put +them into literal character strings. In C, this is done as follows: +.IP +.ft 3 +static char rcsid[] = \&"$\&Id$\&"; +.ft +.LP +The command +.B ident +extracts such markers from any file, even object code +and dumps. +Thus, +.B ident +lets you find out +which revisions of which modules were used in a given program. +.PP +You may also find it useful to put the marker +.B $\&Log$ +into your text, inside a comment. This marker accumulates +the log messages that are requested during check-in. +Thus, you can maintain the complete history of your file directly inside it. +There are several additional identification markers; see +.BR co (1) +for +details. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.br diff --git a/gnu/usr.bin/rcs/rcsclean/Makefile b/gnu/usr.bin/rcs/rcsclean/Makefile new file mode 100644 index 00000000000..fe538a0a41f --- /dev/null +++ b/gnu/usr.bin/rcs/rcsclean/Makefile @@ -0,0 +1,8 @@ +PROG= rcsclean +SRCS= rcsclean.c +CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/rcsclean/rcsclean.1 b/gnu/usr.bin/rcs/rcsclean/rcsclean.1 new file mode 100644 index 00000000000..3111915455a --- /dev/null +++ b/gnu/usr.bin/rcs/rcsclean/rcsclean.1 @@ -0,0 +1,203 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSCLEAN 1 \*(Dt GNU +.SH NAME +rcsclean \- clean up working files +.SH SYNOPSIS +.B rcsclean +.RI [ options "] [ " file " .\|.\|. ]" +.SH DESCRIPTION +.B rcsclean +removes files that are not being worked on. +.B "rcsclean \-u" +also unlocks and removes files that are being worked on +but have not changed. +.PP +For each +.I file +given, +.B rcsclean +compares the working file and a revision in the corresponding +\*r file. If it finds a difference, it does nothing. +Otherwise, it first unlocks the revision if the +.B \-u +option is given, +and then removes the working file +unless the working file is writable and the revision is locked. +It logs its actions by outputting the corresponding +.B "rcs \-u" +and +.B "rm \-f" +commands on the standard output. +.PP +Files are paired as explained in +.BR ci (1). +If no +.I file +is given, all working files in the current directory are cleaned. +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +.PP +The number of the revision to which the working file is compared +may be attached to any of the options +.BR \-n , +.BR \-q , +.BR \-r , +or +.BR \-u . +If no revision number is specified, then if the +.B \-u +option is given and the caller has one revision locked, +.B rcsclean +uses that revision; otherwise +.B rcsclean +uses the latest revision on the default branch, normally the root. +.PP +.B rcsclean +is useful for +.B clean +targets in makefiles. +See also +.BR rcsdiff (1), +which prints out the differences, +and +.BR ci (1), +which +normally reverts to the previous revision +if a file was not changed. +.SH OPTIONS +.TP +.BI \-k subst +Use +.I subst +style keyword substitution when retrieving the revision for comparison. +See +.BR co (1) +for details. +.TP +.BR \-n [\f2rev\fP] +Do not actually remove any files or unlock any revisions. +Using this option will tell you what +.B rcsclean +would do without actually doing it. +.TP +.BR \-q [\f2rev\fP] +Do not log the actions taken on standard output. +.TP +.BR \-r [\f2rev\fP] +This option has no effect other than specifying the revision for comparison. +.TP +.B \-T +Preserve the modification time on the \*r file +even if the \*r file changes because a lock is removed. +This option can suppress extensive recompilation caused by a +.BR make (1) +dependency of some other copy of the working file on the \*r file. +Use this option with care; it can suppress recompilation even when it is needed, +i.e. when the lock removal +would mean a change to keyword strings in the other working file. +.TP +.BR \-u [\f2rev\fP] +Unlock the revision if it is locked and no difference is found. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.TP +.BI \-z zone +Use +.I zone +as the time zone for keyword substitution; +see +.BR co (1) +for details. +.SH EXAMPLES +.LP +.RS +.ft 3 +rcsclean *.c *.h +.ft +.RE +.LP +removes all working files ending in +.B .c +or +.B .h +that were not changed +since their checkout. +.LP +.RS +.ft 3 +rcsclean +.ft +.RE +.LP +removes all working files in the current directory +that were not changed since their checkout. +.SH FILES +.B rcsclean +accesses files much as +.BR ci (1) +does. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +A backslash escapes spaces within an option. +The +.B \s-1RCSINIT\s0 +options are prepended to the argument lists of most \*r commands. +Useful +.B \s-1RCSINIT\s0 +options include +.BR \-q , +.BR \-V , +.BR \-x , +and +.BR \-z . +.SH DIAGNOSTICS +The exit status is zero if and only if all operations were successful. +Missing working files and \*r files are silently ignored. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH BUGS +At least one +.I file +must be given in older Unix versions that +do not provide the needed directory scanning operations. +.br diff --git a/gnu/usr.bin/rcs/rcsclean/rcsclean.c b/gnu/usr.bin/rcs/rcsclean/rcsclean.c new file mode 100644 index 00000000000..32c8e7dfbc6 --- /dev/null +++ b/gnu/usr.bin/rcs/rcsclean/rcsclean.c @@ -0,0 +1,333 @@ +/* Clean up working files. */ + +/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +#if has_dirent + static int get_directory P((char const*,char***)); +#endif + +static int unlock P((struct hshentry *)); +static void cleanup P((void)); + +static RILE *workptr; +static int exitstatus; + +mainProg(rcscleanId, "rcsclean", "$FreeBSD$") +{ + static char const usage[] = + "\nrcsclean: usage: rcsclean -ksubst -{nqru}[rev] -T -Vn -xsuff -zzone file ..."; + + static struct buf revision; + + char *a, **newargv; + char const *rev, *p; + int dounlock, expmode, perform, unlocked, unlockflag, waslocked; + int Ttimeflag; + struct hshentries *deltas; + struct hshentry *delta; + struct stat workstat; + + setrid(); + + expmode = -1; + rev = 0; + suffixes = X_DEFAULT; + perform = true; + unlockflag = false; + Ttimeflag = false; + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + for (;;) { + if (--argc < 1) { +# if has_dirent + argc = get_directory(".", &newargv); + argv = newargv; + break; +# else + faterror("no pathnames specified"); +# endif + } + a = *++argv; + if (!*a || *a++ != '-') + break; + switch (*a++) { + case 'k': + if (0 <= expmode) + redefined('k'); + if ((expmode = str2expmode(a)) < 0) + goto unknown; + break; + + case 'n': + perform = false; + goto handle_revision; + + case 'q': + quietflag = true; + /* fall into */ + case 'r': + handle_revision: + if (*a) { + if (rev) + warn("redefinition of revision number"); + rev = a; + } + break; + + case 'T': + if (*a) + goto unknown; + Ttimeflag = true; + break; + + case 'u': + unlockflag = true; + goto handle_revision; + + case 'V': + setRCSversion(*argv); + break; + + case 'x': + suffixes = a; + break; + + case 'z': + zone_set(a); + break; + + default: + unknown: + error("unknown option: %s%s", *argv, usage); + } + } + + dounlock = perform & unlockflag; + + if (nerror) + cleanup(); + else + for (; 0 < argc; cleanup(), ++argv, --argc) { + + ffree(); + + if (!( + 0 < pairnames( + argc, argv, + dounlock ? rcswriteopen : rcsreadopen, + true, true + ) && + (workptr = Iopen(workname, FOPEN_R_WORK, &workstat)) + )) + continue; + + if (same_file(RCSstat, workstat, 0)) { + rcserror("RCS file is the same as working file %s.", + workname + ); + continue; + } + + gettree(); + + p = 0; + if (rev) { + if (!fexpandsym(rev, &revision, workptr)) + continue; + p = revision.string; + } else if (Head) + switch (unlockflag ? findlock(false,&delta) : 0) { + default: + continue; + case 0: + p = Dbranch ? Dbranch : ""; + break; + case 1: + p = delta->num; + break; + } + delta = 0; + deltas = 0; /* Keep lint happy. */ + if (p && !(delta = genrevs(p,(char*)0,(char*)0,(char*)0,&deltas))) + continue; + + waslocked = delta && delta->lockedby; + locker_expansion = unlock(delta); + unlocked = locker_expansion & unlockflag; + if (unlockednum, RCSname); + + if (perform & unlocked) { + if_advise_access(deltas->first != delta, finptr, MADV_SEQUENTIAL); + if (donerewrite(true, + Ttimeflag ? RCSstat.st_mtime : (time_t)-1 + ) != 0) + continue; + } + + if (!quietflag) + aprintf(stdout, "rm -f %s\n", workname); + Izclose(&workptr); + if (perform && un_link(workname) != 0) + eerror(workname); + + } + + tempunlink(); + if (!quietflag) + Ofclose(stdout); + exitmain(exitstatus); +} + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); + Izclose(&workptr); + Ozclose(&fcopy); + ORCSclose(); + dirtempunlink(); +} + +#if RCS_lint +# define exiterr rcscleanExit +#endif + void +exiterr() +{ + ORCSerror(); + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + + static int +unlock(delta) + struct hshentry *delta; +{ + register struct rcslock **al, *l; + + if (delta && delta->lockedby && strcmp(getcaller(),delta->lockedby)==0) + for (al = &Locks; (l = *al); al = &l->nextlock) + if (l->delta == delta) { + *al = l->nextlock; + delta->lockedby = 0; + return true; + } + return false; +} + +#if has_dirent + static int +get_directory(dirname, aargv) + char const *dirname; + char ***aargv; +/* + * Put a vector of all DIRNAME's directory entries names into *AARGV. + * Ignore names of RCS files. + * Yield the number of entries found. Terminate the vector with 0. + * Allocate the storage for the vector and entry names. + * Do not sort the names. Do not include '.' and '..'. + */ +{ + int i, entries = 0, entries_max = 64; + size_t chars = 0, chars_max = 1024; + size_t *offset = tnalloc(size_t, entries_max); + char *a = tnalloc(char, chars_max), **p; + DIR *d; + struct dirent *e; + + if (!(d = opendir(dirname))) + efaterror(dirname); + while ((errno = 0, e = readdir(d))) { + char const *en = e->d_name; + size_t s = strlen(en) + 1; + if (en[0]=='.' && (!en[1] || (en[1]=='.' && !en[2]))) + continue; + if (rcssuffix(en)) + continue; + while (chars_max < s + chars) + a = trealloc(char, a, chars_max<<=1); + if (entries == entries_max) + offset = trealloc(size_t, offset, entries_max<<=1); + offset[entries++] = chars; + VOID strcpy(a+chars, en); + chars += s; + } +# if void_closedir +# define close_directory(d) (closedir(d), 0) +# else +# define close_directory(d) closedir(d) +# endif + if (errno || close_directory(d) != 0) + efaterror(dirname); + if (chars) + a = trealloc(char, a, chars); + else + tfree(a); + *aargv = p = tnalloc(char*, entries+1); + for (i=0; i diff --git a/gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 b/gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 new file mode 100644 index 00000000000..74117c270e6 --- /dev/null +++ b/gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 @@ -0,0 +1,158 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSDIFF 1 \*(Dt GNU +.SH NAME +rcsdiff \- compare RCS revisions +.SH SYNOPSIS +.B rcsdiff +[ +.BI \-k subst +] [ +.B \-q +] [ +.BI \-r rev1 +[ +.BI \-r rev2 +] ] [ +.B \-T +] [ +.RI "\f3\-V\fP[" n ] +] [ +.BI \-x suffixes +] [ +.BI \-z zone +] [ +.I "diff options" +] +.I "file .\|.\|." +.SH DESCRIPTION +.B rcsdiff +runs +.BR diff (1) +to compare two revisions of each \*r file given. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +.PP +The option +.B \-q +suppresses diagnostic output. +Zero, one, or two revisions may be specified with +.BR \-r . +The option +.BI \-k subst +affects keyword substitution when extracting +revisions, as described in +.BR co (1); +for example, +.B "\-kk\ \-r1.1\ \-r1.2" +ignores differences in keyword values when comparing revisions +.B 1.1 +and +.BR 1.2 . +To avoid excess output from locker name substitution, +.B \-kkvl +is assumed if (1) at most one revision option is given, +(2) no +.B \-k +option is given, (3) +.B \-kkv +is the default keyword substitution, and +(4) the working file's mode would be produced by +.BR "co\ \-l". +See +.BR co (1) +for details +about +.BR \-T , +.BR \-V , +.B \-x +and +.BR \-z . +Otherwise, all options of +.BR diff (1) +that apply to regular files are accepted, with the same meaning as for +.BR diff . +.PP +If both +.I rev1 +and +.I rev2 +are omitted, +.B rcsdiff +compares the latest revision on the +default branch (by default the trunk) +with the contents of the corresponding working file. This is useful +for determining what you changed since the last checkin. +.PP +If +.I rev1 +is given, but +.I rev2 +is omitted, +.B rcsdiff +compares revision +.I rev1 +of the \*r file with +the contents of the corresponding working file. +.PP +If both +.I rev1 +and +.I rev2 +are given, +.B rcsdiff +compares revisions +.I rev1 +and +.I rev2 +of the \*r file. +.PP +Both +.I rev1 +and +.I rev2 +may be given numerically or symbolically. +.SH EXAMPLE +The command +.LP +.B " rcsdiff f.c" +.LP +compares the latest revision on the default branch of the \*r file +to the contents of the working file +.BR f.c . +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +Exit status is 0 for no differences during any comparison, +1 for some differences, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), diff(1), ident(1), rcs(1), rcsintro(1), rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.br diff --git a/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c b/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c new file mode 100644 index 00000000000..7d3c0a37f66 --- /dev/null +++ b/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c @@ -0,0 +1,480 @@ +/* Compare RCS revisions. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (main): Pass "--binary" if -kb and if --binary makes a difference. + * Don't treat + options specially. + * + * Revision 5.17 1994/03/17 14:05:48 eggert + * Specify subprocess input via file descriptor, not file name. Remove lint. + * + * Revision 5.16 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.15 1993/11/03 17:42:27 eggert + * Add -z. Ignore -T. Pass -Vn to `co'. Add Name keyword. + * Put revision numbers in -c output. Improve quality of diagnostics. + * + * Revision 5.14 1992/07/28 16:12:44 eggert + * Add -V. Use co -M for better dates with traditional diff -c. + * + * Revision 5.13 1992/02/17 23:02:23 eggert + * Output more readable context diff headers. + * Suppress needless checkout and comparison of identical revisions. + * + * Revision 5.12 1992/01/24 18:44:19 eggert + * Add GNU diff 1.15.2's new options. lint -> RCS_lint + * + * Revision 5.11 1992/01/06 02:42:34 eggert + * Update usage string. + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Remove lint. + * + * Revision 5.9 1991/08/19 03:13:55 eggert + * Add RCSINIT, -r$. Tune. + * + * Revision 5.8 1991/04/21 11:58:21 eggert + * Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.7 1990/12/13 06:54:07 eggert + * GNU diff 1.15 has -u. + * + * Revision 5.6 1990/11/01 05:03:39 eggert + * Remove unneeded setid check. + * + * Revision 5.5 1990/10/04 06:30:19 eggert + * Accumulate exit status across files. + * + * Revision 5.4 1990/09/27 01:31:43 eggert + * Yield 1, not EXIT_FAILURE, when diffs are found. + * + * Revision 5.3 1990/09/11 02:41:11 eggert + * Simplify -kkvl test. + * + * Revision 5.2 1990/09/04 17:07:19 eggert + * Diff's argv was too small by 1. + * + * Revision 5.1 1990/08/29 07:13:55 eggert + * Add -kkvl. + * + * Revision 5.0 1990/08/22 08:12:46 eggert + * Add -k, -V. Don't use access(). Add setuid support. + * Remove compile-time limits; use malloc instead. + * Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options. + * Add GNU diff's flags. Make lock and temp files faster and safer. + * Ansify and Posixate. + * + * Revision 4.6 89/05/01 15:12:27 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/09 19:12:41 eggert + * Use execv(), not system(); yield exit status like diff(1)s; allow cc -R. + * + * Revision 4.4 87/12/18 11:37:46 narten + * changes Jay Lepreau made in the 4.3 BSD version, to add support for + * "-i", "-w", and "-t" flags and to permit flags to be bundled together, + * merged in. + * + * Revision 4.3 87/10/18 10:31:42 narten + * Updating version numbers. Changes relative to 1.1 actually + * relative to 4.1 + * + * Revision 1.3 87/09/24 13:59:21 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:15 jenkins + * Port to suns + * + * Revision 4.1 83/05/03 22:13:19 wft + * Added default branch, option -q, exit status like diff. + * Added fterror() to replace faterror(). + * + * Revision 3.6 83/01/15 17:52:40 wft + * Expanded mainprogram to handle multiple RCS files. + * + * Revision 3.5 83/01/06 09:33:45 wft + * Fixed passing of -c (context) option to diff. + * + * Revision 3.4 82/12/24 15:28:38 wft + * Added call to catchsig(). + * + * Revision 3.3 82/12/10 16:08:17 wft + * Corrected checking of return code from diff; improved error msgs. + * + * Revision 3.2 82/12/04 13:20:09 wft + * replaced getdelta() with gettree(). Changed diagnostics. + * + * Revision 3.1 82/11/28 19:25:04 wft + * Initial revision. + * + */ +#include "rcsbase.h" + +#if DIFF_L +static char const *setup_label P((struct buf*,char const*,char const[datesize])); +#endif +static void cleanup P((void)); + +static int exitstatus; +static RILE *workptr; +static struct stat workstat; + +mainProg(rcsdiffId, "rcsdiff", "$FreeBSD$") +{ + static char const cmdusage[] = + "\nrcsdiff usage: rcsdiff -ksubst -q -rrev1 [-rrev2] -Vn -xsuff -zzone [diff options] file ..."; + + int revnums; /* counter for revision numbers given */ + char const *rev1, *rev2; /* revision numbers from command line */ + char const *xrev1, *xrev2; /* expanded revision numbers */ + char const *expandarg, *lexpandarg, *suffixarg, *versionarg, *zonearg; +#if DIFF_L + static struct buf labelbuf[2]; + int file_labels; + char const **diff_label1, **diff_label2; + char date2[datesize]; +#endif + char const *cov[10 + !DIFF_L]; + char const **diffv, **diffp, **diffpend; /* argv for subsidiary diff */ + char const **pp, *p, *diffvstr; + struct buf commarg; + struct buf numericrev; /* expanded revision number */ + struct hshentries *gendeltas; /* deltas to be generated */ + struct hshentry * target; + char *a, *dcp, **newargv; + int no_diff_means_no_output; + register c; + + exitstatus = DIFF_SUCCESS; + + bufautobegin(&commarg); + bufautobegin(&numericrev); + revnums = 0; + rev1 = rev2 = xrev2 = 0; +#if DIFF_L + file_labels = 0; +#endif + expandarg = suffixarg = versionarg = zonearg = 0; + no_diff_means_no_output = true; + suffixes = X_DEFAULT; + + /* + * Room for runv extra + args [+ --binary] [+ 2 labels] + * + 1 file + 1 trailing null. + */ + diffv = tnalloc(char const*, 1 + argc + !!OPEN_O_BINARY + 2*DIFF_L + 2); + diffp = diffv + 1; + *diffp++ = DIFF; + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + while (a = *++argv, 0<--argc && *a++=='-') { + dcp = a; + while ((c = *a++)) switch (c) { + case 'r': + switch (++revnums) { + case 1: rev1=a; break; + case 2: rev2=a; break; + default: error("too many revision numbers"); + } + goto option_handled; + case '-': case 'D': + no_diff_means_no_output = false; + /* fall into */ + case 'C': case 'F': case 'I': case 'L': case 'W': +#if DIFF_L + if (c == 'L' && file_labels++ == 2) + faterror("too many -L options"); +#endif + *dcp++ = c; + if (*a) + do *dcp++ = *a++; + while (*a); + else { + if (!--argc) + faterror("-%c needs following argument%s", + c, cmdusage + ); + *diffp++ = *argv++; + } + break; + case 'y': + no_diff_means_no_output = false; + /* fall into */ + case 'B': case 'H': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'h': case 'i': case 'n': case 'p': + case 't': case 'u': case 'w': + *dcp++ = c; + break; + case 'q': + quietflag=true; + break; + case 'x': + suffixarg = *argv; + suffixes = *argv + 2; + goto option_handled; + case 'z': + zonearg = *argv; + zone_set(*argv + 2); + goto option_handled; + case 'T': + /* Ignore -T, so that RCSINIT can contain -T. */ + if (*a) + goto unknown; + break; + case 'V': + versionarg = *argv; + setRCSversion(versionarg); + goto option_handled; + case 'k': + expandarg = *argv; + if (0 <= str2expmode(expandarg+2)) + goto option_handled; + /* fall into */ + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + }; + option_handled: + if (dcp != *argv+1) { + *dcp = 0; + *diffp++ = *argv; + } + } /* end of option processing */ + + for (pp = diffv+2, c = 0; ppnum; + + if (!fexpandsym(rev1, &numericrev, workptr)) continue; + if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue; + xrev1=target->num; +#if DIFF_L + if (diff_label1) + *diff_label1 = setup_label(&labelbuf[0], target->num, target->date); +#endif + + lexpandarg = expandarg; + if (revnums==2) { + if (!fexpandsym( + *rev2 ? rev2 : Dbranch ? Dbranch : Head->num, + &numericrev, + workptr + )) + continue; + if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue; + xrev2=target->num; + if (no_diff_means_no_output && xrev1 == xrev2) + continue; + } else if ( + target->lockedby + && !lexpandarg + && Expand == KEYVAL_EXPAND + && WORKMODE(RCSstat.st_mode,true) == workstat.st_mode + ) + lexpandarg = "-kkvl"; + Izclose(&workptr); +#if DIFF_L + if (diff_label2) + if (revnums == 2) + *diff_label2 = setup_label(&labelbuf[1], target->num, target->date); + else { + time2date(workstat.st_mtime, date2); + *diff_label2 = setup_label(&labelbuf[1], (char*)0, date2); + } +#endif + + diagnose("retrieving revision %s\n", xrev1); + bufscpy(&commarg, "-p"); + bufscat(&commarg, rev1); /* not xrev1, for $Name's sake */ + + pp = &cov[3 + !DIFF_L]; + *pp++ = commarg.string; + if (lexpandarg) *pp++ = lexpandarg; + if (suffixarg) *pp++ = suffixarg; + if (versionarg) *pp++ = versionarg; + if (zonearg) *pp++ = zonearg; + *pp++ = RCSname; + *pp = 0; + + diffp = diffpend; +# if OPEN_O_BINARY + if (Expand == BINARY_EXPAND) + *diffp++ = "--binary"; +# endif + diffp[0] = maketemp(0); + if (runv(-1, diffp[0], cov)) { + rcserror("co failed"); + continue; + } + if (!rev2) { + diffp[1] = workname; + if (*workname == '-') { + char *dp = ftnalloc(char, strlen(workname)+3); + diffp[1] = dp; + *dp++ = '.'; + *dp++ = SLASH; + VOID strcpy(dp, workname); + } + } else { + diagnose("retrieving revision %s\n",xrev2); + bufscpy(&commarg, "-p"); + bufscat(&commarg, rev2); /* not xrev2, for $Name's sake */ + cov[3 + !DIFF_L] = commarg.string; + diffp[1] = maketemp(1); + if (runv(-1, diffp[1], cov)) { + rcserror("co failed"); + continue; + } + } + if (!rev2) + diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workname); + else + diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2); + + diffp[2] = 0; + switch (runv(-1, (char*)0, diffv)) { + case DIFF_SUCCESS: + break; + case DIFF_FAILURE: + if (exitstatus == DIFF_SUCCESS) + exitstatus = DIFF_FAILURE; + break; + default: + workerror("diff failed"); + } + } + + tempunlink(); + exitmain(exitstatus); +} + + static void +cleanup() +{ + if (nerror) exitstatus = DIFF_TROUBLE; + Izclose(&finptr); + Izclose(&workptr); +} + +#if RCS_lint +# define exiterr rdiffExit +#endif + void +exiterr() +{ + tempunlink(); + _exit(DIFF_TROUBLE); +} + +#if DIFF_L + static char const * +setup_label(b, num, date) + struct buf *b; + char const *num; + char const date[datesize]; +{ + char *p; + char datestr[datesize + zonelenmax]; + VOID date2str(date, datestr); + bufalloc(b, + strlen(workname) + + sizeof datestr + 4 + + (num ? strlen(num) : 0) + ); + p = b->string; + if (num) + VOID sprintf(p, "-L%s\t%s\t%s", workname, datestr, num); + else + VOID sprintf(p, "-L%s\t%s", workname, datestr); + return p; +} +#endif diff --git a/gnu/usr.bin/rcs/rcsfreeze/Makefile b/gnu/usr.bin/rcs/rcsfreeze/Makefile new file mode 100644 index 00000000000..1b249c737f1 --- /dev/null +++ b/gnu/usr.bin/rcs/rcsfreeze/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +SCRIPTS= rcsfreeze.sh +MAN= rcsfreeze.1 + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.1 b/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.1 new file mode 100644 index 00000000000..3f513c4d65a --- /dev/null +++ b/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.1 @@ -0,0 +1,68 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds r \s-1RCS\s0 +.TH RCSFREEZE 1 \*(Dt GNU +.SH NAME +rcsfreeze \- freeze a configuration of sources checked in under RCS +.SH SYNOPSIS +.B rcsfreeze +.RI [ "name" ] +.SH DESCRIPTION +.B rcsfreeze +assigns a symbolic revision +number to a set of \*r files that form a valid configuration. +.PP +The idea is to run +.B rcsfreeze +each time a new version is checked +in. A unique symbolic name (\c +.BI C_ number, +where +.I number +is increased each time +.B rcsfreeze +is run) is then assigned to the most +recent revision of each \*r file of the main trunk. +.PP +An optional +.I name +argument to +.B rcsfreeze +gives a symbolic name to the configuration. +The unique identifier is still generated +and is listed in the log file but it will not appear as +part of the symbolic revision name in the actual \*r files. +.PP +A log message is requested from the user for future reference. +.PP +The shell script works only on all \*r files at one time. +All changed files must be checked in already. +Run +.IR rcsclean (1) +first and see whether any sources remain in the current directory. +.SH FILES +.TP +.B RCS/.rcsfreeze.ver +version number +.TP +.B RCS/.rcsfreeze.log +log messages, most recent first +.SH AUTHOR +Stephan v. Bechtolsheim +.SH "SEE ALSO" +co(1), rcs(1), rcsclean(1), rlog(1) +.SH BUGS +.B rcsfreeze +does not check whether any sources are checked out and modified. +.PP +Although both source file names and RCS file names are accepted, +they are not paired as usual with RCS commands. +.PP +Error checking is rudimentary. +.PP +.B rcsfreeze +is just an optional example shell script, and should not be taken too seriously. +See \s-1CVS\s0 for a more complete solution. diff --git a/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh b/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh new file mode 100644 index 00000000000..be79406fe0c --- /dev/null +++ b/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh @@ -0,0 +1,99 @@ +#! /bin/sh + +# rcsfreeze - assign a symbolic revision number to a configuration of RCS files + +# $FreeBSD$ + +# The idea is to run rcsfreeze each time a new version is checked +# in. A unique symbolic revision number (C_[number], where number +# is increased each time rcsfreeze is run) is then assigned to the most +# recent revision of each RCS file of the main trunk. +# +# If the command is invoked with an argument, then this +# argument is used as the symbolic name to freeze a configuration. +# The unique identifier is still generated +# and is listed in the log file but it will not appear as +# part of the symbolic revision name in the actual RCS file. +# +# A log message is requested from the user which is saved for future +# references. +# +# The shell script works only on all RCS files at one time. +# It is important that all changed files are checked in (there are +# no precautions against any error in this respect). +# file names: +# {RCS/}.rcsfreeze.ver version number +# {RCS/}.rscfreeze.log log messages, most recent first + +PATH=/bin:/usr/bin:$PATH +export PATH + +DATE=`LC_ALL=C date` || exit +# Check whether we have an RCS subdirectory, so we can have the right +# prefix for our paths. +if test -d RCS +then RCSDIR=RCS/ EXT= +else RCSDIR= EXT=,v +fi + +# Version number stuff, log message file +VERSIONFILE=${RCSDIR}.rcsfreeze.ver +LOGFILE=${RCSDIR}.rcsfreeze.log +# Initialize, rcsfreeze never run before in the current directory +test -r $VERSIONFILE || { echo 0 >$VERSIONFILE && >>$LOGFILE; } || exit + +# Get Version number, increase it, write back to file. +VERSIONNUMBER=`cat $VERSIONFILE` && +VERSIONNUMBER=`expr $VERSIONNUMBER + 1` && +echo $VERSIONNUMBER >$VERSIONFILE || exit + +# Symbolic Revision Number +SYMREV=C_$VERSIONNUMBER +# Allow the user to give a meaningful symbolic name to the revision. +SYMREVNAME=${1-$SYMREV} +echo >&2 "rcsfreeze: symbolic revision number computed: \"${SYMREV}\" +rcsfreeze: symbolic revision number used: \"${SYMREVNAME}\" +rcsfreeze: the two differ only when rcsfreeze invoked with argument +rcsfreeze: give log message, summarizing changes (end with EOF or single '.')" \ + || exit + +# Stamp the logfile. Because we order the logfile the most recent +# first we will have to save everything right now in a temporary file. +TMPLOG=/tmp/rcsfrz$$ +trap 'rm -f $TMPLOG; exit 1' 1 2 13 15 +# Now ask for a log message, continously add to the log file +( + echo "Version: $SYMREVNAME($SYMREV), Date: $DATE +-----------" || exit + while read MESS + do + case $MESS in + .) break + esac + echo " $MESS" || exit + done + echo "----------- +" && + cat $LOGFILE +) >$TMPLOG && + +# combine old and new logfiles +cp $TMPLOG $LOGFILE && +rm -f $TMPLOG && + +# Now the real work begins by assigning a symbolic revision number +# to each rcs file. Take the most recent version on the default branch. + +# If there are any .*,v files, throw them in too. +# But ignore RCS/.* files that do not end in ,v. +DOTFILES= +for DOTFILE in ${RCSDIR}.*,v +do + if test -f "$DOTFILE" + then + DOTFILES="${RCSDIR}.*,v" + break + fi +done + +exec rcs -q -n$SYMREVNAME: ${RCSDIR}*$EXT $DOTFILES diff --git a/gnu/usr.bin/rcs/rcsmerge/Makefile b/gnu/usr.bin/rcs/rcsmerge/Makefile new file mode 100644 index 00000000000..9fd8afaab02 --- /dev/null +++ b/gnu/usr.bin/rcs/rcsmerge/Makefile @@ -0,0 +1,8 @@ +PROG= rcsmerge +SRCS= rcsmerge.c +CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 b/gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 new file mode 100644 index 00000000000..aff6f666dab --- /dev/null +++ b/gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 @@ -0,0 +1,189 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSMERGE 1 \*(Dt GNU +.SH NAME +rcsmerge \- merge RCS revisions +.SH SYNOPSIS +.B rcsmerge +.RI [ options ] " file" +.SH DESCRIPTION +.B rcsmerge +incorporates the changes between two revisions +of an \*r file into the corresponding working file. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +.PP +At least one revision must be specified with one of the options +described below, usually +.BR \-r . +At most two revisions may be specified. +If only one revision is specified, the latest +revision on the default branch (normally the highest branch on the trunk) +is assumed for the second revision. +Revisions may be specified numerically or symbolically. +.PP +.B rcsmerge +prints a warning if there are overlaps, and delimits +the overlapping regions as explained in +.BR merge (1). +The command is useful for incorporating changes into a checked-out revision. +.SH OPTIONS +.TP +.B \-A +Output conflicts using the +.B \-A +style of +.BR diff3 (1), +if supported by +.BR diff3 . +This merges all changes leading from +.I file2 +to +.I file3 +into +.IR file1 , +and generates the most verbose output. +.TP +\f3\-E\fP, \f3\-e\fP +These options specify conflict styles that generate less information +than +.BR \-A . +See +.BR diff3 (1) +for details. +The default is +.BR \-E . +With +.BR \-e , +.B rcsmerge +does not warn about conflicts. +.TP +.BI \-k subst +Use +.I subst +style keyword substitution. +See +.BR co (1) +for details. +For example, +.B "\-kk\ \-r1.1\ \-r1.2" +ignores differences in keyword values when merging the changes from +.B 1.1 +to +.BR 1.2 . +It normally does not make sense to merge binary files as if they were text, so +.B rcsmerge +refuses to merge files if +.B \-kb +expansion is used. +.TP +.BR \-p [\f2rev\fP] +Send the result to standard output instead of overwriting the working file. +.TP +.BR \-q [\f2rev\fP] +Run quietly; do not print diagnostics. +.TP +.BR \-r [\f2rev\fP] +Merge with respect to revision +.IR rev . +Here an empty +.I rev +stands for the latest revision on the default branch, normally the head. +.TP +.B \-T +This option has no effect; +it is present for compatibility with other \*r commands. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.TP +.BI \-z zone +Use +.I zone +as the time zone for keyword substitution. +See +.BR co (1) +for details. +.SH EXAMPLES +Suppose you have released revision 2.8 of +.BR f.c . +Assume +furthermore that after you complete an unreleased revision 3.4, you receive +updates to release 2.8 from someone else. +To combine the updates to 2.8 and your changes between 2.8 and 3.4, +put the updates to 2.8 into file f.c and execute +.LP +.B " rcsmerge \-p \-r2.8 \-r3.4 f.c >f.merged.c" +.PP +Then examine +.BR f.merged.c . +Alternatively, if you want to save the updates to 2.8 in the \*r file, +check them in as revision 2.8.1.1 and execute +.BR "co \-j": +.LP +.B " ci \-r2.8.1.1 f.c" +.br +.B " co \-r3.4 \-j2.8:2.8.1.1 f.c" +.PP +As another example, the following command undoes the changes +between revision 2.4 and 2.8 in your currently checked out revision +in +.BR f.c . +.LP +.B " rcsmerge \-r2.8 \-r2.4 f.c" +.PP +Note the order of the arguments, and that +.B f.c +will be +overwritten. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), merge(1), rcs(1), rcsdiff(1), rcsintro(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.br diff --git a/gnu/usr.bin/rcs/rcsmerge/rcsmerge.c b/gnu/usr.bin/rcs/rcsmerge/rcsmerge.c new file mode 100644 index 00000000000..e09dc246986 --- /dev/null +++ b/gnu/usr.bin/rcs/rcsmerge/rcsmerge.c @@ -0,0 +1,286 @@ +/* Merge RCS revisions. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.15 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.14 1995/06/01 16:23:43 eggert + * (main): Report an error if -kb, so don't worry about binary stdout. + * Punctuate messages properly. Rewrite to avoid `goto end'. + * + * Revision 5.13 1994/03/17 14:05:48 eggert + * Specify subprocess input via file descriptor, not file name. Remove lint. + * + * Revision 5.12 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.11 1993/11/03 17:42:27 eggert + * Add -A, -E, -e, -z. Ignore -T. Allow up to three file labels. + * Pass -Vn to `co'. Pass unexpanded revision name to `co', so that Name works. + * + * Revision 5.10 1992/07/28 16:12:44 eggert + * Add -V. + * + * Revision 5.9 1992/01/24 18:44:19 eggert + * lint -> RCS_lint + * + * Revision 5.8 1992/01/06 02:42:34 eggert + * Update usage string. + * + * Revision 5.7 1991/11/20 17:58:09 eggert + * Don't Iopen(f, "r+"); it's not portable. + * + * Revision 5.6 1991/08/19 03:13:55 eggert + * Add -r$. Tune. + * + * Revision 5.5 1991/04/21 11:58:27 eggert + * Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.4 1991/02/25 07:12:43 eggert + * Merging a revision to itself is no longer an error. + * + * Revision 5.3 1990/11/01 05:03:50 eggert + * Remove unneeded setid check. + * + * Revision 5.2 1990/09/04 08:02:28 eggert + * Check for I/O error when reading working file. + * + * Revision 5.1 1990/08/29 07:14:04 eggert + * Add -q. Pass -L options to merge. + * + * Revision 5.0 1990/08/22 08:13:41 eggert + * Propagate merge's exit status. + * Remove compile-time limits; use malloc instead. + * Make lock and temp files faster and safer. Ansify and Posixate. Add -V. + * Don't use access(). Tune. + * + * Revision 4.5 89/05/01 15:13:16 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 88/08/09 19:13:13 eggert + * Beware merging into a readonly file. + * Beware merging a revision to itself (no change). + * Use execv(), not system(); yield exit status like diff(1)'s. + * + * Revision 4.3 87/10/18 10:38:02 narten + * Updating version numbers. Changes relative to version 1.1 + * actually relative to 4.1 + * + * Revision 1.3 87/09/24 14:00:31 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:36 jenkins + * Port to suns + * + * Revision 4.1 83/03/28 11:14:57 wft + * Added handling of default branch. + * + * Revision 3.3 82/12/24 15:29:00 wft + * Added call to catchsig(). + * + * Revision 3.2 82/12/10 21:32:02 wft + * Replaced getdelta() with gettree(); improved error messages. + * + * Revision 3.1 82/11/28 19:27:44 wft + * Initial revision. + * + */ +#include "rcsbase.h" + +static char const co[] = CO; + +mainProg(rcsmergeId, "rcsmerge", "$FreeBSD$") +{ + static char const cmdusage[] = + "\nrcsmerge usage: rcsmerge -rrev1 [-rrev2] -ksubst -{pq}[rev] -Vn -xsuff -zzone file"; + static char const quietarg[] = "-q"; + + register int i; + char *a, **newargv; + char const *arg[3]; + char const *rev[3], *xrev[3]; /*revision numbers*/ + char const *edarg, *expandarg, *suffixarg, *versionarg, *zonearg; + int tostdout; + int status; + RILE *workptr; + struct buf commarg; + struct buf numericrev; /* holds expanded revision number */ + struct hshentries *gendeltas; /* deltas to be generated */ + struct hshentry * target; + + bufautobegin(&commarg); + bufautobegin(&numericrev); + edarg = rev[1] = rev[2] = 0; + status = 0; /* Keep lint happy. */ + tostdout = false; + expandarg = suffixarg = versionarg = zonearg = quietarg; /* no-op */ + suffixes = X_DEFAULT; + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + while (a = *++argv, 0<--argc && *a++=='-') { + switch (*a++) { + case 'p': + tostdout=true; + goto revno; + + case 'q': + quietflag = true; + revno: + if (!*a) + break; + /* falls into -r */ + case 'r': + if (!rev[1]) + rev[1] = a; + else if (!rev[2]) + rev[2] = a; + else + error("too many revision numbers"); + break; + + case 'A': case 'E': case 'e': + if (*a) + goto unknown; + edarg = *argv; + break; + + case 'x': + suffixarg = *argv; + suffixes = a; + break; + case 'z': + zonearg = *argv; + zone_set(a); + break; + case 'T': + /* Ignore -T, so that RCSINIT can contain -T. */ + if (*a) + goto unknown; + break; + case 'V': + versionarg = *argv; + setRCSversion(versionarg); + break; + + case 'k': + expandarg = *argv; + if (0 <= str2expmode(expandarg+2)) + break; + /* fall into */ + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + }; + } /* end of option processing */ + + if (!rev[1]) faterror("no base revision number given"); + + /* Now handle all pathnames. */ + + if (!nerror) { + if (argc < 1) + faterror("no input file%s", cmdusage); + if (0 < pairnames(argc, argv, rcsreadopen, true, false)) { + + if (argc>2 || (argc==2 && argv[1])) + warn("excess arguments ignored"); + if (Expand == BINARY_EXPAND) + workerror("merging binary files"); + diagnose("RCS file: %s\n", RCSname); + if (!(workptr = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) + efaterror(workname); + + gettree(); /* reads in the delta tree */ + + if (!Head) rcsfaterror("no revisions present"); + + if (!*rev[1]) + rev[1] = Dbranch ? Dbranch : Head->num; + if (fexpandsym(rev[1], &numericrev, workptr) + && (target=genrevs(numericrev.string, (char *)0, (char *)0, (char*)0, &gendeltas)) + ) { + xrev[1] = target->num; + if (!rev[2] || !*rev[2]) + rev[2] = Dbranch ? Dbranch : Head->num; + if (fexpandsym(rev[2], &numericrev, workptr) + && (target=genrevs(numericrev.string, (char *)0, (char *)0, (char *)0, &gendeltas)) + ) { + xrev[2] = target->num; + + if (strcmp(xrev[1],xrev[2]) == 0) { + if (tostdout) { + fastcopy(workptr, stdout); + Ofclose(stdout); + } + } else { + Izclose(&workptr); + + for (i=1; i<=2; i++) { + diagnose("retrieving revision %s\n", xrev[i]); + bufscpy(&commarg, "-p"); + bufscat(&commarg, rev[i]); /* not xrev[i], for $Name's sake */ + if (run( + -1, + /* Do not collide with merger.c maketemp(). */ + arg[i] = maketemp(i+2), + co, quietarg, commarg.string, + expandarg, suffixarg, versionarg, zonearg, + RCSname, (char*)0 + )) + rcsfaterror("co failed"); + } + diagnose("Merging differences between %s and %s into %s%s\n", + xrev[1], xrev[2], workname, + tostdout?"; result to stdout":""); + + arg[0] = xrev[0] = workname; + status = merge(tostdout, edarg, xrev, arg); + } + } + } + + Izclose(&workptr); + } + } + tempunlink(); + exitmain(nerror ? DIFF_TROUBLE : status); +} + +#if RCS_lint +# define exiterr rmergeExit +#endif + void +exiterr() +{ + tempunlink(); + _exit(DIFF_TROUBLE); +} diff --git a/gnu/usr.bin/rcs/rcstest b/gnu/usr.bin/rcs/rcstest new file mode 100755 index 00000000000..75165f1bbd5 --- /dev/null +++ b/gnu/usr.bin/rcs/rcstest @@ -0,0 +1,454 @@ +#! /bin/sh + +# Test RCS's functions. +# The RCS commands are searched for in the PATH as usual; +# to test the working directory's commands, prepend . to your PATH. + +# Test RCS by creating files RCS/a.* and RCS/a.c. +# If all goes well, output nothing, and remove the temporary files. +# Otherwise, send a message to standard output. +# Exit status is 0 if OK, 1 if an RCS bug is found, and 2 if scaffolding fails. +# With the -v option, output more debugging info. + +# If diff outputs `No differences encountered' when comparing identical files, +# then rcstest may also output these noise lines; ignore them. + +# The current directory and ./RCS must be readable, writable, and searchable. + +# $FreeBSD$ + + +# Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert +# Distributed under license by the Free Software Foundation, Inc. +# +# This file is part of RCS. +# +# RCS is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# RCS is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with RCS; see the file COPYING. +# If not, write to the Free Software Foundation, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu + +# The Makefile overrides the following defaults. +: ${ALL_CFLAGS=-Dhas_conf_h} +: ${CC=cc} +: ${DIFF=diff} +# : ${LDFLAGS=} ${LIBS=} tickles old shell bug + +CL="$CC $ALL_CFLAGS $LDFLAGS -o a.out" +L=$LIBS + +RCSINIT=-x +export RCSINIT + +SLASH=/ +RCSfile=RCS${SLASH}a.c +RCS_alt=RCS${SLASH}a.d +lockfile=RCS${SLASH}a._ + +case $1 in +-v) q=; set -x;; +'') q=-q;; +*) echo >&2 "$0: usage: $0 [-v]"; exit 2 +esac + +if test -d RCS +then rmdir=: +else rmdir=rmdir; mkdir RCS || exit +fi + +rm -f a.* $RCSfile $RCS_alt $lockfile && +echo 1.1 >a.11 && +echo 1.1.1.1 >a.3x1 && +echo 1.2 >a.12 || { echo "#initialization failed"; exit 2; } + +case "`$DIFF -c a.11 a.3x1`" in +*!\ 1.1.1.1) + diff="$DIFF -c";; +*) + echo "#warning: $DIFF -c does not work, so diagnostics may be cryptic" + diff=$DIFF +esac + +rcs -i -L -ta.11 $q a.c && +test -r $RCSfile || { + echo "#rcs -i -L failed; perhaps RCS is not properly installed." + exit 1 +} + +rlog a.c >/dev/null || { echo "#rlog failed on empty RCS file"; exit 1; } +rm -f $RCSfile || exit 2 + +cp a.11 a.c && +ci -ta.11 -mm $q a.c && +test -r $RCSfile && +rcs -L $q a.c || { echo "#ci+rcs -L failed"; exit 1; } +test ! -f a.c || { echo "#ci did not remove working file"; exit 1; } +for l in '' '-l' +do + co $l $q a.c && + test -f a.c || { echo '#co' $l did not create working file; exit 1; } + $diff a.11 a.c || { echo '#ci' followed by co $l is not a no-op; exit 1; } +done + +cp a.12 a.c && +ci -mm $q a.c && +co $q a.c && +$diff a.12 a.c || { echo "#ci+co failed"; exit 1; } + +rm -f a.c && +co -r1.1 $q a.c && +$diff a.11 a.c || { echo "#can't retrieve first revision"; exit 1; } + +rm -f a.c && +cp a.3x1 a.c && +ci -r1.1.1 -mm $q a.c && +co -r1.1.1.1 $q a.c && +$diff a.3x1 a.c || { echo "#branches failed"; exit 1; } + +rm -f a.c && +co -l $q a.c && +ci -f -mm $q a.c && +co -r1.3 $q a.c && +$diff a.12 a.c || { echo "#(co -l; ci -f) failed"; exit 1; } + +rm -f a.c && +co -l $q a.c && +echo 1.4 >a.c && +ci -l -mm $q a.c && +echo error >a.c && +ci -mm $q a.c || { echo "#ci -l failed"; exit 1; } + +rm -f a.c && +co -l $q a.c && +echo 1.5 >a.c && +ci -u -mm $q a.c && +test -r a.c || { echo "#ci -u didn't create a working file"; exit 1; } +rm -f a.c && +echo error >a.c || exit 2 +ci -mm $q a.c 2>/dev/null && { echo "#ci -u didn't unlock the file"; exit 1; } + +rm -f a.c && +rcs -l $q a.c && +co -u $q a.c || { echo "#rcs -l + co -u failed"; exit 1; } +rm -f a.c && +echo error >a.c || exit 2 +ci -mm $q a.c 2>/dev/null && { echo "#co -u didn't unlock the file"; exit 1; } + +rm -f a.c && +cp a.11 a.c && +co -f $q a.c || { echo "#co -f failed"; exit 1; } +$diff a.11 a.c >/dev/null && { echo "#co -f had no effect"; exit 1; } + +co -p1.1 $q a.c >a.t && +$diff a.11 a.t || { echo "#co -p failed"; exit 1; } + +for n in n N +do + rm -f a.c && + co -l $q a.c && + echo $n >a.$n && + cp a.$n a.c && + ci -${n}n -mm $q a.c && + co -rn $q a.c && + $diff a.$n a.c || { echo "#ci -$n failed"; exit 1; } +done + +case $LOGNAME in +?*) me=$LOGNAME;; +*) + case $USER in + ?*) me=$USER;; + *) + me=`who am i` || exit 2 + me=`echo "$me" | sed -e 's/ .*//' -e 's/.*!//'` + case $me in + '') echo >&2 "$0: cannot deduce user name"; exit 2 + esac + esac +esac + + +# Get the date of the previous revision in UTC. +date=`rlog -r a.c | sed -n '/^date: /{ s///; s/;.*//; p; q; }'` || exit +case $date in +[0-9][0-9][0-9]*[0-9]/[0-1][0-9]/[0-3][0-9]\ [0-2][0-9]:[0-5][0-9]:[0-6][0-9]);; +*) echo >&2 "$0: $date: bad rlog date output"; exit 1 +esac +PWD=`pwd` && export PWD && +rm -f a.c && +co -l $q a.c && +sed 's/@/$/g' >a.kv <a.k && +sed -e 's/w s [$]/w s '"$me"' $/' -e 's/[$]Locker: /&'"$me/" a.kv >a.kvl && +sed s/Oz//g a.kv >a.e && +sed s/Oz/N/g a.kv >a.N && +sed -e '/\$/!d' -e 's/\$$/: old $/' a.k >a.o && +sed -e 's/\$[^ ]*: //' -e 's/ \$//' a.kv >a.v && +cp a.o a.c && +ci -d"$date" -nOz -ss -ww -u2.1 -mm $q a.c && +$diff a.kv a.c || { echo "#keyword expansion failed"; exit 1; } +co -pOz -ko $q a.c >a.oo && +$diff a.o a.oo || { echo "#co -p -ko failed"; exit 1; } +cp a.kv a.o && cp a.o a.b || exit 2 +rcs -oOz $q a.c && +rcs -l $q a.c && +ci -k -u $q a.c && +$diff a.kv a.c || { echo "#ci -k failed"; exit 1; } +sed -n 's/^[^$]*\$/$/p' a.kv >a.i && +ident a.c >a.i1 && +sed -e 1d -e 's/^[ ]*//' a.i1 >a.i2 && +$diff a.i a.i2 || { echo "#ident failed"; exit 1; } + +rcs -i $q a.c 2>/dev/null && { echo "#rcs -i permitted existing file"; exit 1; } + +rm -f a.c && +co -l $q a.c && +echo 2.2 >a.c && +ci -mm $q a.c && +echo 1.1.1.2 >a.c && +rcs -l1.1.1 $q a.c && +ci -r1.1.1.2 -mm $q a.c && +rcs -b1.1.1 $q a.c && +test " `co -p $q a.c`" = ' 1.1.1.2' || { echo "#rcs -b1.1.1 failed"; exit 1; } +rcs -b $q a.c && +test " `co -p $q a.c`" = ' 2.2' || { echo "#rcs -b failed"; exit 1; } + +echo 2.3 >a.c || exit 2 +rcs -U $q a.c || { echo "#rcs -U failed"; exit 1; } +ci -mm $q a.c || { echo "#rcs -U didn't unset strict locking"; exit 1; } +rcs -L $q a.c || { echo "#rcs -L failed"; exit 1; } +echo error >a.c || exit 2 +ci -mm $q a.c 2>/dev/null && { echo "#ci retest failed"; exit 1; } + +rm -f a.c && +log0=`rlog -h a.c` && +co -l $q a.c && +ci -mm $q a.c && +log1=`rlog -h a.c` && +test " $log0" = " $log1" || { echo "#unchanged ci didn't revert"; exit 1; } + +rm -f a.c && +rcs -nN:1.1 $q a.c && +co -rN $q a.c && +$diff a.11 a.c || { echo "#rcs -n failed"; exit 1; } + +rm -f a.c && +rcs -NN:2.1 $q a.c && +co -rN $q a.c && +$diff a.N a.c || { echo "#rcs -N failed"; exit 1; } + +rm -f a.c && +co -l $q a.c && +echo ':::$''Log$' >a.c && +ci -u -mm $q a.c && +test " `sed '$!d' a.c`" = ' :::' || { echo "#comment leader failed"; exit 1; } + +rm -f a.c && +rcs -o2.2: $q a.c && +co $q a.c && +$diff a.e a.c || { echo "#rcs -o failed"; exit 1; } + +rcsdiff -r1.1 -rOz $q a.c >a.0 +case $? in +1) ;; +*) echo "#rcsdiff bad status"; exit 1 +esac +$DIFF a.11 a.kv >a.1 +$diff a.0 a.1 || { echo "#rcsdiff failed"; exit 1; } + +rcs -l2.1 $q a.c || { echo "#rcs -l2.1 failed"; exit 1; } +for i in b k kv kvl o v +do + rm -f a.c && + cp a.$i a.c && + rcsdiff -k$i -rOz $q a.c || { echo "#rcsdiff -k$i failed"; exit 1; } +done +co -p1.1 -ko $q a.c >a.t && +$diff a.11 a.t || { echo "#co -p1.1 -ko failed"; exit 1; } +rcs -u2.1 $q a.c || { echo "#rcs -u2.1 failed"; exit 1; } + +rm -f a.c && +rcsclean $q a.c && +rcsclean -u $q a.c || { echo "#rcsclean botched a nonexistent file"; exit 1; } + +rm -f a.c && +co $q a.c && +rcsclean -n $q a.c && +rcsclean -n -u $q a.c && +test -f a.c || { echo "#rcsclean -n removed a file"; exit 1; } + +rm -f a.c && +co $q a.c && +rcsclean $q a.c && +test ! -f a.c || { echo "#rcsclean missed an unlocked file"; exit 1; } + +rm -f a.c && +co -l $q a.c && +rcsclean $q a.c && +test -f a.c || { echo "#rcsclean removed a locked file"; exit 1; } +rcsclean -u $q a.c && +test ! -f a.c || { + echo "#rcsclean -u missed an unchanged locked file"; exit 1; +} + +rm -f a.c && +co -l $q a.c && +echo change >>a.c && +rcsclean $q a.c && +rcsclean $q -u a.c && +test -f a.c || { echo "#rcsclean removed a changed file"; exit 1; } + +rm -f a.c && +co -l $q a.c && +cat >a.c <<'EOF' +2.2 +a +b +c +d +EOF +test $? = 0 && +ci -l -mm $q a.c && +co -p2.2 $q a.c | sed -e s/2.2/2.3/ -e s/b/b1/ >a.c && +ci -l -mm $q a.c && +co -p2.2 $q a.c | sed -e s/2.2/new/ -e s/d/d1/ >a.c || exit 2 +cat >a.0 <<'EOF' +2.3 +a +b1 +c +d1 +EOF +cat >a.1 <<'EOF' +<<<<<<< a.c +new +======= +2.3 +>>>>>>> 2.3 +a +b1 +c +d1 +EOF +rcsmerge -E -r2.2 -r2.3 $q a.c +case $? in +0) + if $diff a.0 a.c >/dev/null + then echo "#warning: diff3 -E does not work, " \ + "so merge and rcsmerge ignore overlaps and suppress overlap lines." + else + $diff a.1 a.c || { echo "#rcsmerge failed (status 0)"; exit 1; } + echo "#warning: The diff3 lib program exit status ignores overlaps," \ + "so rcsmerge does not warn about overlap lines that it generates." + fi + ;; +1) + $diff a.1 a.c || { echo "#rcsmerge failed (status 1)"; exit 1; } + ;; +*) + echo "#rcsmerge bad status"; exit 1 +esac + +# Avoid `tr' if possible; it's not portable, and it can't handle null bytes. +# Our substitute exclusive-ORs with '\n'; +# this ensures null bytes on output, which is even better than `tr', +# since some diffs think a file is binary only if it contains null bytes. +cat >a.c <<'EOF' +#include +int main() { + int c; + while ((c=getchar()) != EOF) + putchar(c ^ '\n'); + return 0; +} +EOF +tr=tr +if (rm -f a.exe a.out && $CL a.c $L >&2) >/dev/null 2>&1 +then + if test -s a.out + then tr=./a.out + elif test -s a.exe + then tr=./a.exe + fi +fi +{ + co -p $q a.c | $tr '\012' '\200' >a.24 && + cp a.24 a.c && + ciOut=`(ci -l -mm $q a.c 2>&1)` && + case $ciOut in + ?*) echo >&2 "$ciOut" + esac && + co -p $q a.c | $tr '\200' '\012' >a.c && + rcsdiff -r2.3 $q a.c >/dev/null && + + echo 2.5 >a.c && + ci -l -mm $q a.c && + cp a.24 a.c && + rcsdiff -r2.4 $q a.c >/dev/null +} || echo "#warning: Traditional diff is used, so RCS is limited to text files." + +rcs -u -o2.4: $q a.c || { echo "#rcs -u -o failed"; exit 1; } + +rcs -i -Aa.c -t- $q a.d || { echo "#rcs -i -A failed"; exit 1; } + +rlog -r2.1 a.c >a.t && +grep '^checked in with -k' a.t >/dev/null && +sed '/^checked in with -k/d' a.t >a.u && +$diff - a.u < diff --git a/gnu/usr.bin/rcs/rlog/rlog.1 b/gnu/usr.bin/rcs/rlog/rlog.1 new file mode 100644 index 00000000000..26bf08a2bcb --- /dev/null +++ b/gnu/usr.bin/rcs/rlog/rlog.1 @@ -0,0 +1,318 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $FreeBSD$ +.ds i \&\s-1ISO\s0 +.ds r \&\s-1RCS\s0 +.ds u \&\s-1UTC\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RLOG 1 \*(Dt GNU +.SH NAME +rlog \- print log messages and other information about RCS files +.SH SYNOPSIS +.B rlog +.RI [ " options " ] " file " .\|.\|. +.SH DESCRIPTION +.B rlog +prints information about \*r files. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +.PP +.B rlog +prints the following information for each +\*r file: \*r pathname, working pathname, head (i.e., the number +of the latest revision on the trunk), default branch, access list, locks, +symbolic names, suffix, total number of revisions, +number of revisions selected for printing, and +descriptive text. This is followed by entries for the selected revisions in +reverse chronological order for each branch. For each revision, +.B rlog +prints revision number, author, date/time, state, number of +lines added/deleted (with respect to the previous revision), +locker of the revision (if any), and log message. +All times are displayed in Coordinated Universal Time (\*u) by default; +this can be overridden with +.BR \-z . +Without options, +.B rlog +prints complete information. +The options below restrict this output. +.nr n \w'\f3\-V\fP\f2n\fP'+2n-1/1n +.ds n \nn +.if \n(.g .if r an-tag-sep .ds n \w'\f3\-V\fP\f2n\fP'u+\n[an-tag-sep]u +.TP \*n +.B \-L +Ignore \*r files that have no locks set. +This is convenient in combination with +.BR \-h , +.BR \-l , +and +.BR \-R . +.TP +.B \-R +Print only the name of the \*r file. +This is convenient for translating a +working pathname into an \*r pathname. +.TP +.BI \-v "[string]" +Print only the working pathname and tip-revision. +The optional string is prepended to the outputline. +.TP +.B \-h +Print only the \*r pathname, working pathname, head, +default branch, access list, locks, +symbolic names, and suffix. +.TP +.B \-t +Print the same as +.BR \-h , +plus the descriptive text. +.TP +.B \-N +Do not print the symbolic names. +.TP +.B \-b +Print information about the revisions on the default branch, normally +the highest branch on the trunk. +.TP +.BI \-d "dates" +Print information about revisions with a checkin date/time in the ranges given by +the semicolon-separated list of +.IR dates . +A range of the form +.IB d1 < d2 +or +.IB d2 > d1 +selects the revisions that were deposited between +.I d1 +and +.I d2 +exclusive. +A range of the form +.BI < d +or +.IB d > +selects +all revisions earlier than +.IR d . +A range of the form +.IB d < +or +.BI > d +selects +all revisions dated later than +.IR d . +If +.B < +or +.B > +is followed by +.B = +then the ranges are inclusive, not exclusive. +A range of the form +.I d +selects the single, latest revision dated +.I d +or earlier. +The date/time strings +.IR d , +.IR d1 , +and +.I d2 +are in the free format explained in +.BR co (1). +Quoting is normally necessary, especially for +.B < +and +.BR > . +Note that the separator is +a semicolon. +.TP +.BR \-l [\f2lockers\fP] +Print information about locked revisions only. +In addition, if the comma-separated list +.I lockers +of login names is given, +ignore all locks other than those held by the +.IR lockers . +For example, +.B "rlog\ \-L\ \-R\ \-lwft\ RCS/*" +prints the name of \*r files locked by the user +.BR wft . +.TP +.BR \-r [\f2revisions\fP] +prints information about revisions given in the comma-separated list +.I revisions +of revisions and ranges. +A range +.IB rev1 : rev2 +means revisions +.I rev1 +to +.I rev2 +on the same branch, +.BI : rev +means revisions from the beginning of the branch up to and including +.IR rev , +and +.IB rev : +means revisions starting with +.I rev +to the end of the branch containing +.IR rev . +An argument that is a branch means all +revisions on that branch. +A range of branches means all revisions +on the branches in that range. +A branch followed by a +.B .\& +means the latest revision in that branch. +A bare +.B \-r +with no +.I revisions +means the latest revision on the default branch, normally the trunk. +.TP +.BI \-s states +prints information about revisions whose state attributes match one of the +states given in the comma-separated list +.IR states . +.TP +.BR \-w [\f2logins\fP] +prints information about revisions checked in by users with +login names appearing in the comma-separated list +.IR logins . +If +.I logins +is omitted, the user's login is assumed. +.TP +.B \-T +This option has no effect; +it is present for compatibility with other \*r commands. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.I n +when generating logs. +See +.BR co (1) +for more. +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.PP +.B rlog +prints the intersection of the revisions selected with +the options +.BR \-d , +.BR \-l , +.BR \-s , +and +.BR \-w , +intersected +with the union of the revisions selected by +.B \-b +and +.BR \-r . +.TP +.BI \-z zone +specifies the date output format, +and specifies the default time zone for +.I date +in the +.BI \-d dates +option. +The +.I zone +should be empty, a numeric \*u offset, or the special string +.B LT +for local time. +The default is an empty +.IR zone , +which uses the traditional \*r format of \*u without any time zone indication +and with slashes separating the parts of the date; +otherwise, times are output in \*i 8601 format with time zone indication. +For example, if local time is January 11, 1990, 8pm Pacific Standard Time, +eight hours west of \*u, +then the time is output as follows: +.RS +.LP +.RS +.nf +.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u +.ne 4 +\f2option\fP \f2time output\fP +\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP +\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP +\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP +.ta 4n +4n +4n +4n +.fi +.RE +.SH EXAMPLES +.LP +.nf +.B " rlog \-L \-R RCS/*" +.B " rlog \-L \-h RCS/*" +.B " rlog \-L \-l RCS/*" +.B " rlog RCS/*" +.fi +.LP +The first command prints the names of all \*r files in the subdirectory +.B RCS +that have locks. The second command prints the headers of those files, +and the third prints the headers plus the log messages of the locked revisions. +The last command prints complete information. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH BUGS +The separator for revision ranges in the +.B \-r +option used to be +.B \- +instead of +.BR : , +but this leads to confusion when symbolic names contain +.BR \- . +For backwards compatibility +.B "rlog \-r" +still supports the old +.B \- +separator, but it warns about this obsolete use. +.br diff --git a/gnu/usr.bin/rcs/rlog/rlog.c b/gnu/usr.bin/rcs/rlog/rlog.c new file mode 100644 index 00000000000..f8872febb97 --- /dev/null +++ b/gnu/usr.bin/rcs/rlog/rlog.c @@ -0,0 +1,1290 @@ +/* Print log messages and other information about RCS files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * Revision 5.18 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.17 1995/06/01 16:23:43 eggert + * (struct rcslockers): Renamed from `struct lockers'. + * (getnumericrev): Return error indication instead of ignoring errors. + * (main): Check it. Don't use dateform. + * (recentdate, extdate): cmpnum -> cmpdate + * + * Revision 5.16 1994/04/13 16:30:34 eggert + * Fix bug; `rlog -lxxx' inverted the sense of -l. + * + * Revision 5.15 1994/03/17 14:05:48 eggert + * -d' RCS_lint + * + * Revision 5.10 1992/01/06 02:42:34 eggert + * Update usage string. + * while (E) ; -> while (E) continue; + * + * Revision 5.9 1991/09/17 19:07:40 eggert + * Getscript() didn't uncache partial lines. + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Revision separator is `:', not `-'. + * Check for missing and duplicate logs. Tune. + * Permit log messages that do not end in newline (including empty logs). + * + * Revision 5.7 1991/04/21 11:58:31 eggert + * Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.6 1991/02/26 17:07:17 eggert + * Survive RCS files with missing logs. + * strsave -> str_save (DG/UX name clash) + * + * Revision 5.5 1990/11/01 05:03:55 eggert + * Permit arbitrary data in logs and comment leaders. + * + * Revision 5.4 1990/10/04 06:30:22 eggert + * Accumulate exit status across files. + * + * Revision 5.3 1990/09/11 02:41:16 eggert + * Plug memory leak. + * + * Revision 5.2 1990/09/04 08:02:33 eggert + * Count RCS lines better. + * + * Revision 5.0 1990/08/22 08:13:48 eggert + * Remove compile-time limits; use malloc instead. Add setuid support. + * Switch to GMT. + * Report dates in long form, to warn about dates past 1999/12/31. + * Change "added/del" message to make room for the longer dates. + * Don't generate trailing white space. Add -V. Ansify and Posixate. + * + * Revision 4.7 89/05/01 15:13:48 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.6 88/08/09 19:13:28 eggert + * Check for memory exhaustion; don't access freed storage. + * Shrink stdio code size; remove lint. + * + * Revision 4.5 87/12/18 11:46:38 narten + * more lint cleanups (Guy Harris) + * + * Revision 4.4 87/10/18 10:41:12 narten + * Updating version numbers + * Changes relative to 1.1 actually relative to 4.2 + * + * Revision 1.3 87/09/24 14:01:10 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:45 jenkins + * Port to suns + * + * Revision 4.2 83/12/05 09:18:09 wft + * changed rewriteflag to external. + * + * Revision 4.1 83/05/11 16:16:55 wft + * Added -b, updated getnumericrev() accordingly. + * Replaced getpwuid() with getcaller(). + * + * Revision 3.7 83/05/11 14:24:13 wft + * Added options -L and -R; + * Fixed selection bug with -l on multiple files. + * Fixed error on dates of the form -d'>date' (rewrote getdatepair()). + * + * Revision 3.6 82/12/24 15:57:53 wft + * shortened output format. + * + * Revision 3.5 82/12/08 21:45:26 wft + * removed call to checkaccesslist(); used DATEFORM to format all dates; + * removed unused variables. + * + * Revision 3.4 82/12/04 13:26:25 wft + * Replaced getdelta() with gettree(); removed updating of field lockedby. + * + * Revision 3.3 82/12/03 14:08:20 wft + * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE. + * Fixed printing of nil, removed printing of Suffix, + * added shortcut if no revisions are printed, disambiguated struct members. + * + * Revision 3.2 82/10/18 21:09:06 wft + * call to curdir replaced with getfullRCSname(), + * fixed call to getlogin(), cosmetic changes on output, + * changed conflicting long identifiers. + * + * Revision 3.1 82/10/13 16:07:56 wft + * fixed type of variables receiving from getc() (char -> int). + */ + + + +#include "rcsbase.h" + +struct rcslockers { /* lockers in locker option; stored */ + char const * login; /* lockerlist */ + struct rcslockers * lockerlink; + } ; + +struct stateattri { /* states in state option; stored in */ + char const * status; /* statelist */ + struct stateattri * nextstate; + } ; + +struct authors { /* login names in author option; */ + char const * login; /* stored in authorlist */ + struct authors * nextauthor; + } ; + +struct Revpairs{ /* revision or branch range in -r */ + int numfld; /* option; stored in revlist */ + char const * strtrev; + char const * endrev; + struct Revpairs * rnext; + } ; + +struct Datepairs{ /* date range in -d option; stored in */ + struct Datepairs *dnext; + char strtdate[datesize]; /* duelst and datelist */ + char enddate[datesize]; + char ne_date; /* datelist only; distinguishes < from <= */ + }; + +static char extractdelta P((struct hshentry const*)); +static int checkrevpair P((char const*,char const*)); +static int extdate P((struct hshentry*)); +static int getnumericrev P((void)); +static struct hshentry const *readdeltalog P((void)); +static void cleanup P((void)); +static void exttree P((struct hshentry*)); +static void getauthor P((char*)); +static void getdatepair P((char*)); +static void getlocker P((char*)); +static void getrevpairs P((char*)); +static void getscript P((struct hshentry*)); +static void getstate P((char*)); +static void putabranch P((struct hshentry const*)); +static void putadelta P((struct hshentry const*,struct hshentry const*,int)); +static void putforest P((struct branchhead const*)); +static void putree P((struct hshentry const*)); +static void putrunk P((void)); +static void recentdate P((struct hshentry const*,struct Datepairs*)); +static void trunclocks P((void)); + +static char const *insDelFormat; +static int branchflag; /*set on -b */ +static int exitstatus; +static int lockflag; +static struct Datepairs *datelist, *duelst; +static struct Revpairs *revlist, *Revlst; +static struct authors *authorlist; +static struct rcslockers *lockerlist; +static struct stateattri *statelist; + + +mainProg(rlogId, "rlog", "$FreeBSD$") +{ + static char const cmdusage[] = + "\nrlog usage: rlog -{bhLNRt} -v[string] -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ..."; + + register FILE *out; + char *a, **newargv; + struct Datepairs *currdate; + char const *accessListString, *accessFormat; + char const *headFormat, *symbolFormat; + struct access const *curaccess; + struct assoc const *curassoc; + struct hshentry const *delta; + struct rcslock const *currlock; + int descflag, selectflag; + int onlylockflag; /* print only files with locks */ + int onlyRCSflag; /* print only RCS pathname */ + int pre5; + int shownames; + int revno; + int versionlist; + char *vstring; + + descflag = selectflag = shownames = true; + versionlist = onlylockflag = onlyRCSflag = false; + vstring=0; + out = stdout; + suffixes = X_DEFAULT; + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + while (a = *++argv, 0<--argc && *a++=='-') { + switch (*a++) { + + case 'L': + onlylockflag = true; + break; + + case 'N': + shownames = false; + break; + + case 'R': + onlyRCSflag =true; + break; + + case 'l': + lockflag = true; + getlocker(a); + break; + + case 'b': + branchflag = true; + break; + + case 'r': + getrevpairs(a); + break; + + case 'd': + getdatepair(a); + break; + + case 's': + getstate(a); + break; + + case 'w': + getauthor(a); + break; + + case 'h': + descflag = false; + break; + + case 't': + selectflag = false; + break; + + case 'q': + /* This has no effect; it's here for consistency. */ + quietflag = true; + break; + + case 'x': + suffixes = a; + break; + + case 'z': + zone_set(a); + break; + + case 'T': + /* Ignore -T, so that RCSINIT can contain -T. */ + if (*a) + goto unknown; + break; + + case 'V': + setRCSversion(*argv); + break; + + case 'v': + versionlist = true; + vstring = a; + break; + + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + + }; + } /* end of option processing */ + + if (! (descflag|selectflag)) { + warn("-t overrides -h."); + descflag = true; + } + + pre5 = RCSversion < VERSION(5); + if (pre5) { + accessListString = "\naccess list: "; + accessFormat = " %s"; + headFormat = "RCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: "; + insDelFormat = " lines added/del: %ld/%ld"; + symbolFormat = " %s: %s;"; + } else { + accessListString = "\naccess list:"; + accessFormat = "\n\t%s"; + headFormat = "RCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s"; + insDelFormat = " lines: +%ld -%ld"; + symbolFormat = "\n\t%s: %s"; + } + + /* Now handle all pathnames. */ + if (nerror) + cleanup(); + else if (argc < 1) + faterror("no input file%s", cmdusage); + else + for (; 0 < argc; cleanup(), ++argv, --argc) { + ffree(); + + if (pairnames(argc, argv, rcsreadopen, true, false) <= 0) + continue; + + /* + * RCSname contains the name of the RCS file, + * and finptr the file descriptor; + * workname contains the name of the working file. + */ + + /* Keep only those locks given by -l. */ + if (lockflag) + trunclocks(); + + /* do nothing if -L is given and there are no locks*/ + if (onlylockflag && !Locks) + continue; + + if ( versionlist ) { + gettree(); + aprintf(out, "%s%s %s\n", vstring, workname, tiprev()); + continue; + } + + if ( onlyRCSflag ) { + aprintf(out, "%s\n", RCSname); + continue; + } + + gettree(); + + if (!getnumericrev()) + continue; + + /* + * Output the first character with putc, not printf. + * Otherwise, an SVR4 stdio bug buffers output inefficiently. + */ + aputc_('\n', out) + + /* print RCS pathname, working pathname and optional + administrative information */ + /* could use getfullRCSname() here, but that is very slow */ + aprintf(out, headFormat, RCSname, workname, + Head ? " " : "", Head ? Head->num : "", + Dbranch ? " " : "", Dbranch ? Dbranch : "", + StrictLocks ? " strict" : "" + ); + currlock = Locks; + while( currlock ) { + aprintf(out, symbolFormat, currlock->login, + currlock->delta->num); + currlock = currlock->nextlock; + } + if (StrictLocks && pre5) + aputs(" ; strict" + (Locks?3:0), out); + + aputs(accessListString, out); /* print access list */ + curaccess = AccessList; + while(curaccess) { + aprintf(out, accessFormat, curaccess->login); + curaccess = curaccess->nextaccess; + } + + if (shownames) { + aputs("\nsymbolic names:", out); /* print symbolic names */ + for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc) + aprintf(out, symbolFormat, curassoc->symbol, curassoc->num); + } + if (pre5) { + aputs("\ncomment leader: \"", out); + awrite(Comment.string, Comment.size, out); + afputc('\"', out); + } + if (!pre5 || Expand != KEYVAL_EXPAND) + aprintf(out, "\nkeyword substitution: %s", + expand_names[Expand] + ); + + aprintf(out, "\ntotal revisions: %d", TotalDeltas); + + revno = 0; + + if (Head && selectflag & descflag) { + + exttree(Head); + + /* get most recently date of the dates pointed by duelst */ + currdate = duelst; + while( currdate) { + VOID strcpy(currdate->strtdate, "0.0.0.0.0.0"); + recentdate(Head, currdate); + currdate = currdate->dnext; + } + + revno = extdate(Head); + + aprintf(out, ";\tselected revisions: %d", revno); + } + + afputc('\n',out); + if (descflag) { + aputs("description:\n", out); + getdesc(true); + } + if (revno) { + while (! (delta = readdeltalog())->selector || --revno) + continue; + if (delta->next && countnumflds(delta->num)==2) + /* Read through delta->next to get its insertlns. */ + while (readdeltalog() != delta->next) + continue; + putrunk(); + putree(Head); + } + aputs("----------------------------\n", out); + aputs("=============================================================================\n",out); + } + Ofclose(out); + exitmain(exitstatus); +} + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); +} + +#if RCS_lint +# define exiterr rlogExit +#endif + void +exiterr() +{ + _exit(EXIT_FAILURE); +} + + + + static void +putrunk() +/* function: print revisions chosen, which are in trunk */ + +{ + register struct hshentry const *ptr; + + for (ptr = Head; ptr; ptr = ptr->next) + putadelta(ptr, ptr->next, true); +} + + + + static void +putree(root) + struct hshentry const *root; +/* function: print delta tree (not including trunk) in reverse + order on each branch */ + +{ + if (!root) return; + + putree(root->next); + + putforest(root->branches); +} + + + + + static void +putforest(branchroot) + struct branchhead const *branchroot; +/* function: print branches that has the same direct ancestor */ +{ + if (!branchroot) return; + + putforest(branchroot->nextbranch); + + putabranch(branchroot->hsh); + putree(branchroot->hsh); +} + + + + + static void +putabranch(root) + struct hshentry const *root; +/* function : print one branch */ + +{ + if (!root) return; + + putabranch(root->next); + + putadelta(root, root, false); +} + + + + + + static void +putadelta(node,editscript,trunk) + register struct hshentry const *node, *editscript; + int trunk; +/* function: Print delta node if node->selector is set. */ +/* editscript indicates where the editscript is stored */ +/* trunk indicated whether this node is in trunk */ +{ + static char emptych[] = EMPTYLOG; + + register FILE *out; + char const *s; + size_t n; + struct branchhead const *newbranch; + struct buf branchnum; + char datebuf[datesize + zonelenmax]; + int pre5 = RCSversion < VERSION(5); + + if (!node->selector) + return; + + out = stdout; + aprintf(out, + "----------------------------\nrevision %s%s", + node->num, pre5 ? " " : "" + ); + if ( node->lockedby ) + aprintf(out, pre5+"\tlocked by: %s;", node->lockedby); + + aprintf(out, "\ndate: %s; author: %s; state: %s;", + date2str(node->date, datebuf), + node->author, node->state + ); + + if ( editscript ) + if(trunk) + aprintf(out, insDelFormat, + editscript->deletelns, editscript->insertlns); + else + aprintf(out, insDelFormat, + editscript->insertlns, editscript->deletelns); + + newbranch = node->branches; + if ( newbranch ) { + bufautobegin(&branchnum); + aputs("\nbranches:", out); + while( newbranch ) { + getbranchno(newbranch->hsh->num, &branchnum); + aprintf(out, " %s;", branchnum.string); + newbranch = newbranch->nextbranch; + } + bufautoend(&branchnum); + } + + afputc('\n', out); + s = node->log.string; + if (!(n = node->log.size)) { + s = emptych; + n = sizeof(emptych)-1; + } + awrite(s, n, out); + if (s[n-1] != '\n') + afputc('\n', out); +} + + + static struct hshentry const * +readdeltalog() +/* Function : get the log message and skip the text of a deltatext node. + * Return the delta found. + * Assumes the current lexeme is not yet in nexttok; does not + * advance nexttok. + */ +{ + register struct hshentry * Delta; + struct buf logbuf; + struct cbuf cb; + + if (eoflex()) + fatserror("missing delta log"); + nextlex(); + if (!(Delta = getnum())) + fatserror("delta number corrupted"); + getkeystring(Klog); + if (Delta->log.string) + fatserror("duplicate delta log"); + bufautobegin(&logbuf); + cb = savestring(&logbuf); + Delta->log = bufremember(&logbuf, cb.size); + + ignorephrases(Ktext); + getkeystring(Ktext); + Delta->insertlns = Delta->deletelns = 0; + if ( Delta != Head) + getscript(Delta); + else + readstring(); + return Delta; +} + + + static void +getscript(Delta) +struct hshentry * Delta; +/* function: read edit script of Delta and count how many lines added */ +/* and deleted in the script */ + +{ + int ed; /* editor command */ + declarecache; + register RILE *fin; + register int c; + register long i; + struct diffcmd dc; + + fin = finptr; + setupcache(fin); + initdiffcmd(&dc); + while (0 <= (ed = getdiffcmd(fin,true,(FILE *)0,&dc))) + if (!ed) + Delta->deletelns += dc.nlines; + else { + /* skip scripted lines */ + i = dc.nlines; + Delta->insertlns += i; + cache(fin); + do { + for (;;) { + cacheget_(c) + switch (c) { + default: + continue; + case SDELIM: + cacheget_(c) + if (c == SDELIM) + continue; + if (--i) + unexpected_EOF(); + nextc = c; + uncache(fin); + return; + case '\n': + break; + } + break; + } + ++rcsline; + } while (--i); + uncache(fin); + } +} + + + + + + + + static void +exttree(root) +struct hshentry *root; +/* function: select revisions , starting with root */ + +{ + struct branchhead const *newbranch; + + if (!root) return; + + root->selector = extractdelta(root); + root->log.string = 0; + exttree(root->next); + + newbranch = root->branches; + while( newbranch ) { + exttree(newbranch->hsh); + newbranch = newbranch->nextbranch; + } +} + + + + + static void +getlocker(argv) +char * argv; +/* function : get the login names of lockers from command line */ +/* and store in lockerlist. */ + +{ + register char c; + struct rcslockers *newlocker; + argv--; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + if ( c == '\0') { + lockerlist = 0; + return; + } + + while( c != '\0' ) { + newlocker = talloc(struct rcslockers); + newlocker->lockerlink = lockerlist; + newlocker->login = argv; + lockerlist = newlocker; + while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; + *argv = '\0'; + if ( c == '\0' ) return; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + } +} + + + + static void +getauthor(argv) +char *argv; +/* function: get the author's name from command line */ +/* and store in authorlist */ + +{ + register c; + struct authors * newauthor; + + argv--; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + if ( c == '\0' ) { + authorlist = talloc(struct authors); + authorlist->login = getusername(false); + authorlist->nextauthor = 0; + return; + } + + while( c != '\0' ) { + newauthor = talloc(struct authors); + newauthor->nextauthor = authorlist; + newauthor->login = argv; + authorlist = newauthor; + while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; + * argv = '\0'; + if ( c == '\0') return; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + } +} + + + + + static void +getstate(argv) +char * argv; +/* function : get the states of revisions from command line */ +/* and store in statelist */ + +{ + register char c; + struct stateattri *newstate; + + argv--; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + if ( c == '\0'){ + error("missing state attributes after -s options"); + return; + } + + while( c != '\0' ) { + newstate = talloc(struct stateattri); + newstate->nextstate = statelist; + newstate->status = argv; + statelist = newstate; + while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; + *argv = '\0'; + if ( c == '\0' ) return; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + } +} + + + + static void +trunclocks() +/* Function: Truncate the list of locks to those that are held by the */ +/* id's on lockerlist. Do not truncate if lockerlist empty. */ + +{ + struct rcslockers const *plocker; + struct rcslock *p, **pp; + + if (!lockerlist) return; + + /* shorten Locks to those contained in lockerlist */ + for (pp = &Locks; (p = *pp); ) + for (plocker = lockerlist; ; ) + if (strcmp(plocker->login, p->login) == 0) { + pp = &p->nextlock; + break; + } else if (!(plocker = plocker->lockerlink)) { + *pp = p->nextlock; + break; + } +} + + + + static void +recentdate(root, pd) + struct hshentry const *root; + struct Datepairs *pd; +/* function: Finds the delta that is closest to the cutoff date given by */ +/* pd among the revisions selected by exttree. */ +/* Successively narrows down the interval given by pd, */ +/* and sets the strtdate of pd to the date of the selected delta */ +{ + struct branchhead const *newbranch; + + if (!root) return; + if (root->selector) { + if ( cmpdate(root->date, pd->strtdate) >= 0 && + cmpdate(root->date, pd->enddate) <= 0) + VOID strcpy(pd->strtdate, root->date); + } + + recentdate(root->next, pd); + newbranch = root->branches; + while( newbranch) { + recentdate(newbranch->hsh, pd); + newbranch = newbranch->nextbranch; + } +} + + + + + + + static int +extdate(root) +struct hshentry * root; +/* function: select revisions which are in the date range specified */ +/* in duelst and datelist, start at root */ +/* Yield number of revisions selected, including those already selected. */ +{ + struct branchhead const *newbranch; + struct Datepairs const *pdate; + int revno, ne; + + if (!root) + return 0; + + if ( datelist || duelst) { + pdate = datelist; + while( pdate ) { + ne = pdate->ne_date; + if ( + (!pdate->strtdate[0] + || ne <= cmpdate(root->date, pdate->strtdate)) + && + (!pdate->enddate[0] + || ne <= cmpdate(pdate->enddate, root->date)) + ) + break; + pdate = pdate->dnext; + } + if (!pdate) { + pdate = duelst; + for (;;) { + if (!pdate) { + root->selector = false; + break; + } + if (cmpdate(root->date, pdate->strtdate) == 0) + break; + pdate = pdate->dnext; + } + } + } + revno = root->selector + extdate(root->next); + + newbranch = root->branches; + while( newbranch ) { + revno += extdate(newbranch->hsh); + newbranch = newbranch->nextbranch; + } + return revno; +} + + + + static char +extractdelta(pdelta) + struct hshentry const *pdelta; +/* function: compare information of pdelta to the authorlist, lockerlist,*/ +/* statelist, revlist and yield true if pdelta is selected. */ + +{ + struct rcslock const *plock; + struct stateattri const *pstate; + struct authors const *pauthor; + struct Revpairs const *prevision; + int length; + + if ((pauthor = authorlist)) /* only certain authors wanted */ + while (strcmp(pauthor->login, pdelta->author) != 0) + if (!(pauthor = pauthor->nextauthor)) + return false; + if ((pstate = statelist)) /* only certain states wanted */ + while (strcmp(pstate->status, pdelta->state) != 0) + if (!(pstate = pstate->nextstate)) + return false; + if (lockflag) /* only locked revisions wanted */ + for (plock = Locks; ; plock = plock->nextlock) + if (!plock) + return false; + else if (plock->delta == pdelta) + break; + if ((prevision = Revlst)) /* only certain revs or branches wanted */ + for (;;) { + length = prevision->numfld; + if ( + countnumflds(pdelta->num) == length+(length&1) && + 0 <= compartial(pdelta->num, prevision->strtrev, length) && + 0 <= compartial(prevision->endrev, pdelta->num, length) + ) + break; + if (!(prevision = prevision->rnext)) + return false; + } + return true; +} + + + + static void +getdatepair(argv) + char * argv; +/* function: get time range from command line and store in datelist if */ +/* a time range specified or in duelst if a time spot specified */ + +{ + register char c; + struct Datepairs * nextdate; + char const * rawdate; + int switchflag; + + argv--; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + if ( c == '\0' ) { + error("missing date/time after -d"); + return; + } + + while( c != '\0' ) { + switchflag = false; + nextdate = talloc(struct Datepairs); + if ( c == '<' ) { /* case: -d ne_date = c!='=')) + c = *++argv; + (nextdate->strtdate)[0] = '\0'; + } else if (c == '>') { /* case: -d'>date' */ + c = *++argv; + if (!(nextdate->ne_date = c!='=')) + c = *++argv; + (nextdate->enddate)[0] = '\0'; + switchflag = true; + } else { + rawdate = argv; + while( c != '<' && c != '>' && c != ';' && c != '\0') + c = *++argv; + *argv = '\0'; + if ( c == '>' ) switchflag=true; + str2date(rawdate, + switchflag ? nextdate->enddate : nextdate->strtdate); + if ( c == ';' || c == '\0') { /* case: -d date */ + VOID strcpy(nextdate->enddate,nextdate->strtdate); + nextdate->dnext = duelst; + duelst = nextdate; + goto end; + } else { + /* case: -d date< or -d date>; see switchflag */ + int eq = argv[1]=='='; + nextdate->ne_date = !eq; + argv += eq; + while ((c = *++argv) == ' ' || c=='\t' || c=='\n') + continue; + if ( c == ';' || c == '\0') { + /* second date missing */ + if (switchflag) + *nextdate->strtdate= '\0'; + else + *nextdate->enddate= '\0'; + nextdate->dnext = datelist; + datelist = nextdate; + goto end; + } + } + } + rawdate = argv; + while( c != '>' && c != '<' && c != ';' && c != '\0') + c = *++argv; + *argv = '\0'; + str2date(rawdate, + switchflag ? nextdate->strtdate : nextdate->enddate); + nextdate->dnext = datelist; + datelist = nextdate; + end: + if (RCSversion < VERSION(5)) + nextdate->ne_date = 0; + if ( c == '\0') return; + while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n') + continue; + } +} + + + + static int +getnumericrev() +/* function: get the numeric name of revisions which stored in revlist */ +/* and then stored the numeric names in Revlst */ +/* if branchflag, also add default branch */ + +{ + struct Revpairs * ptr, *pt; + int n; + struct buf s, e; + char const *lrev; + struct buf const *rstart, *rend; + + Revlst = 0; + ptr = revlist; + bufautobegin(&s); + bufautobegin(&e); + while( ptr ) { + n = 0; + rstart = &s; + rend = &e; + + switch (ptr->numfld) { + + case 1: /* -rREV */ + if (!expandsym(ptr->strtrev, &s)) + goto freebufs; + rend = &s; + n = countnumflds(s.string); + if (!n && (lrev = tiprev())) { + bufscpy(&s, lrev); + n = countnumflds(lrev); + } + break; + + case 2: /* -rREV: */ + if (!expandsym(ptr->strtrev, &s)) + goto freebufs; + bufscpy(&e, s.string); + n = countnumflds(s.string); + (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0; + break; + + case 3: /* -r:REV */ + if (!expandsym(ptr->endrev, &e)) + goto freebufs; + if ((n = countnumflds(e.string)) < 2) + bufscpy(&s, ".0"); + else { + bufscpy(&s, e.string); + VOID strcpy(strrchr(s.string,'.'), ".0"); + } + break; + + default: /* -rREV1:REV2 */ + if (!( + expandsym(ptr->strtrev, &s) + && expandsym(ptr->endrev, &e) + && checkrevpair(s.string, e.string) + )) + goto freebufs; + n = countnumflds(s.string); + /* Swap if out of order. */ + if (compartial(s.string,e.string,n) > 0) { + rstart = &e; + rend = &s; + } + break; + } + + if (n) { + pt = ftalloc(struct Revpairs); + pt->numfld = n; + pt->strtrev = fstr_save(rstart->string); + pt->endrev = fstr_save(rend->string); + pt->rnext = Revlst; + Revlst = pt; + } + ptr = ptr->rnext; + } + /* Now take care of branchflag */ + if (branchflag && (Dbranch||Head)) { + pt = ftalloc(struct Revpairs); + pt->strtrev = pt->endrev = + Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1)); + pt->rnext=Revlst; Revlst=pt; + pt->numfld = countnumflds(pt->strtrev); + } + + freebufs: + bufautoend(&s); + bufautoend(&e); + return !ptr; +} + + + + static int +checkrevpair(num1,num2) + char const *num1, *num2; +/* function: check whether num1, num2 are legal pair,i.e. + only the last field are different and have same number of + fields( if length <= 2, may be different if first field) */ + +{ + int length = countnumflds(num1); + + if ( + countnumflds(num2) != length + || (2 < length && compartial(num1, num2, length-1) != 0) + ) { + rcserror("invalid branch or revision pair %s : %s", num1, num2); + return false; + } + + return true; +} + + + + static void +getrevpairs(argv) +register char * argv; +/* function: get revision or branch range from command line, and */ +/* store in revlist */ + +{ + register char c; + struct Revpairs * nextrevpair; + int separator; + + c = *argv; + + /* Support old ambiguous '-' syntax; this will go away. */ + if (strchr(argv,':')) + separator = ':'; + else { + if (strchr(argv,'-') && VERSION(5) <= RCSversion) + warn("`-' is obsolete in `-r%s'; use `:' instead", argv); + separator = '-'; + } + + for (;;) { + while (c==' ' || c=='\t' || c=='\n') + c = *++argv; + nextrevpair = talloc(struct Revpairs); + nextrevpair->rnext = revlist; + revlist = nextrevpair; + nextrevpair->numfld = 1; + nextrevpair->strtrev = argv; + for (;; c = *++argv) { + switch (c) { + default: + continue; + case '\0': case ' ': case '\t': case '\n': + case ',': case ';': + break; + case ':': case '-': + if (c == separator) + break; + continue; + } + break; + } + *argv = '\0'; + while (c==' ' || c=='\t' || c=='\n') + c = *++argv; + if (c == separator) { + while ((c = *++argv) == ' ' || c == '\t' || c =='\n') + continue; + nextrevpair->endrev = argv; + for (;; c = *++argv) { + switch (c) { + default: + continue; + case '\0': case ' ': case '\t': case '\n': + case ',': case ';': + break; + case ':': case '-': + if (c == separator) + break; + continue; + } + break; + } + *argv = '\0'; + while (c==' ' || c=='\t' || c =='\n') + c = *++argv; + nextrevpair->numfld = + !nextrevpair->endrev[0] ? 2 /* -rREV: */ : + !nextrevpair->strtrev[0] ? 3 /* -r:REV */ : + 4 /* -rREV1:REV2 */; + } + if (!c) + break; + else if (c==',' || c==';') + c = *++argv; + else + error("missing `,' near `%c%s'", c, argv+1); + } +} diff --git a/share/doc/psd/13.rcs/Makefile b/share/doc/psd/13.rcs/Makefile new file mode 100644 index 00000000000..ed497da4d8d --- /dev/null +++ b/share/doc/psd/13.rcs/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +SUBDIR= rcs rcs_func + +.include + diff --git a/share/doc/psd/13.rcs/Makefile.inc b/share/doc/psd/13.rcs/Makefile.inc new file mode 100644 index 00000000000..666dbd862ff --- /dev/null +++ b/share/doc/psd/13.rcs/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +VOLUME= psd/13.rcs +MACROS= -ms +SRCDIR= ${.CURDIR}/../../../../../gnu/usr.bin/rcs/doc diff --git a/share/doc/psd/13.rcs/rcs/Makefile b/share/doc/psd/13.rcs/rcs/Makefile new file mode 100644 index 00000000000..6d94aed2bb4 --- /dev/null +++ b/share/doc/psd/13.rcs/rcs/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +SRCS= rcs.ms +USE_PIC= +USE_TBL= + +.include diff --git a/share/doc/psd/13.rcs/rcs_func/Makefile b/share/doc/psd/13.rcs/rcs_func/Makefile new file mode 100644 index 00000000000..09e5a9b226c --- /dev/null +++ b/share/doc/psd/13.rcs/rcs_func/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +DOC= rcs_func +SRCS= rcs_func.ms + +.include diff --git a/share/doc/psd/Makefile b/share/doc/psd/Makefile index 0222c64ea68..243ba9992e2 100644 --- a/share/doc/psd/Makefile +++ b/share/doc/psd/Makefile @@ -20,6 +20,7 @@ SUBDIR= title \ 05.sysman \ 06.Clang \ 12.make \ + 13.rcs \ 15.yacc \ 16.lex \ 17.m4 \ diff --git a/share/man/man5/src.conf.5 b/share/man/man5/src.conf.5 index a176bb75b64..aad4004f5a6 100644 --- a/share/man/man5/src.conf.5 +++ b/share/man/man5/src.conf.5 @@ -909,6 +909,11 @@ This includes .Xr rlogin 1 , .Xr rsh 1 , etc. +.It Va WITHOUT_RCS +.\" from FreeBSD: head/tools/build/options/WITHOUT_RCS 156932 2006-03-21 07:50:50Z ru +Set to not build +.Xr rcs 1 +and related utilities. .It Va WITHOUT_RESCUE .\" from FreeBSD: head/tools/build/options/WITHOUT_RESCUE 156932 2006-03-21 07:50:50Z ru Set to not build diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index 5642c6edad7..a1b6c4412c8 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -334,6 +334,7 @@ __DEFAULT_YES_OPTIONS = \ PROFILE \ QUOTAS \ RCMDS \ + RCS \ RESCUE \ ROUTED \ SENDMAIL \ diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index 1c9fce0e813..dada14d115b 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -3859,6 +3859,31 @@ OLD_FILES+=usr/share/man/man8/rshd.8.gz OLD_FILES+=usr/share/man/man8/rwhod.8.gz .endif +.if ${MK_RCS} == no +OLD_FILES+=usr/bin/ci +OLD_FILES+=usr/bin/co +OLD_FILES+=usr/bin/ident +OLD_FILES+=usr/bin/merge +OLD_FILES+=usr/bin/rcs +OLD_FILES+=usr/bin/rcsclean +OLD_FILES+=usr/bin/rcsdiff +OLD_FILES+=usr/bin/rcsfreeze +OLD_FILES+=usr/bin/rcsmerge +OLD_FILES+=usr/bin/rlog +OLD_FILES+=usr/share/man/man1/ci.1.gz +OLD_FILES+=usr/share/man/man1/co.1.gz +OLD_FILES+=usr/share/man/man1/ident.1.gz +OLD_FILES+=usr/share/man/man1/merge.1.gz +OLD_FILES+=usr/share/man/man1/rcs.1.gz +OLD_FILES+=usr/share/man/man1/rcsclean.1.gz +OLD_FILES+=usr/share/man/man1/rcsdiff.1.gz +OLD_FILES+=usr/share/man/man1/rcsfreeze.1.gz +OLD_FILES+=usr/share/man/man1/rcsintro.1.gz +OLD_FILES+=usr/share/man/man1/rcsmerge.1.gz +OLD_FILES+=usr/share/man/man1/rlog.1.gz +OLD_FILES+=usr/share/man/man5/rcsfile.5.gz +.endif + #.if ${MK_RESCUE} == no # to be filled in or replaced with a special target #.endif diff --git a/tools/build/options/WITHOUT_RCS b/tools/build/options/WITHOUT_RCS new file mode 100644 index 00000000000..2a4ddecab44 --- /dev/null +++ b/tools/build/options/WITHOUT_RCS @@ -0,0 +1,4 @@ +.\" $FreeBSD$ +Set to not build +.Xr rcs 1 +and related utilities. From 209536b767320dfa9f895f964271c80a1b521a07 Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Wed, 9 Oct 2013 17:07:50 +0000 Subject: [PATCH 16/18] Initialize a variable in sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c, to silence a gcc warning. Approved by: re (gjb) X-MFC-With: r255332 --- sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c b/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c index 4b453c037d0..29aec0044f3 100644 --- a/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c @@ -159,7 +159,7 @@ ipf_p_rcmd_portmsg(fin, aps, nat) ip6_t *ip6; #endif int tcpsz; - int slen; + int slen = 0; /* silence gcc */ ip_t *ip; mb_t *m; From 7609433eb60b1aee7ac039dc519e8293262f4013 Mon Sep 17 00:00:00 2001 From: Jack F Vogel Date: Wed, 9 Oct 2013 17:32:52 +0000 Subject: [PATCH 17/18] Update the Intel igb driver to version 2.4.0 - This version has support for the new Intel Avoton systems, including 2.5Gb support, further it now has IPv6/TSO6 support as well. Shared code has been updated where necessary as well. Thanks to my new assistant Eric Joyner for doing the transmit path changes to bring in the IPv6/TSO6 support. Thanks to Gleb for catching the one bug and change needed in NETMAP. Approved by: re --- sys/dev/e1000/e1000_80003es2lan.c | 358 +++++------- sys/dev/e1000/e1000_80003es2lan.h | 12 +- sys/dev/e1000/e1000_82571.c | 27 +- sys/dev/e1000/e1000_82575.c | 325 +++++++++-- sys/dev/e1000/e1000_82575.h | 5 + sys/dev/e1000/e1000_api.c | 11 +- sys/dev/e1000/e1000_defines.h | 40 ++ sys/dev/e1000/e1000_hw.h | 16 +- sys/dev/e1000/e1000_i210.c | 319 ++++++---- sys/dev/e1000/e1000_i210.h | 8 +- sys/dev/e1000/e1000_ich8lan.c | 436 +++++++++----- sys/dev/e1000/e1000_ich8lan.h | 50 +- sys/dev/e1000/e1000_mac.c | 40 +- sys/dev/e1000/e1000_osdep.h | 1 + sys/dev/e1000/e1000_phy.c | 285 +++++++-- sys/dev/e1000/e1000_phy.h | 18 +- sys/dev/e1000/e1000_regs.h | 12 +- sys/dev/e1000/e1000_vf.h | 106 ++-- sys/dev/e1000/if_igb.c | 933 ++++++++++++++++-------------- sys/dev/e1000/if_igb.h | 239 ++++---- sys/dev/netmap/if_igb_netmap.h | 2 +- 21 files changed, 2022 insertions(+), 1221 deletions(-) diff --git a/sys/dev/e1000/e1000_80003es2lan.c b/sys/dev/e1000/e1000_80003es2lan.c index bdbb31cfdbb..076e02be9b0 100644 --- a/sys/dev/e1000/e1000_80003es2lan.c +++ b/sys/dev/e1000/e1000_80003es2lan.c @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2013, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,16 +32,12 @@ ******************************************************************************/ /*$FreeBSD$*/ -/* - * 80003ES2LAN Gigabit Ethernet Controller (Copper) +/* 80003ES2LAN Gigabit Ethernet Controller (Copper) * 80003ES2LAN Gigabit Ethernet Controller (Serdes) */ #include "e1000_api.h" -static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw); -static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw); -static s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw); static s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw); static void e1000_release_phy_80003es2lan(struct e1000_hw *hw); static s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw); @@ -71,14 +67,12 @@ static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, u16 *data); static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, u16 data); -static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw); static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw); static void e1000_release_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask); static s32 e1000_read_mac_addr_80003es2lan(struct e1000_hw *hw); static void e1000_power_down_phy_copper_80003es2lan(struct e1000_hw *hw); -/* - * A table for the GG82563 cable length where the range is defined +/* A table for the GG82563 cable length where the range is defined * with a lower bound at "index" and the upper bound at * "index + 5". */ @@ -95,13 +89,13 @@ static const u16 e1000_gg82563_cable_length_table[] = { static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_init_phy_params_80003es2lan"); if (hw->phy.media_type != e1000_media_type_copper) { phy->type = e1000_phy_none; - goto out; + return E1000_SUCCESS; } else { phy->ops.power_up = e1000_power_up_phy_copper; phy->ops.power_down = e1000_power_down_phy_copper_80003es2lan; @@ -133,12 +127,9 @@ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw) ret_val = e1000_get_phy_id(hw); /* Verify phy id */ - if (phy->id != GG82563_E_PHY_ID) { - ret_val = -E1000_ERR_PHY; - goto out; - } + if (phy->id != GG82563_E_PHY_ID) + return -E1000_ERR_PHY; -out: return ret_val; } @@ -176,8 +167,7 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> E1000_EECD_SIZE_EX_SHIFT); - /* - * Added to a constant, "size" becomes the left-shift value + /* Added to a constant, "size" becomes the left-shift value * for setting word_size. */ size += NVM_WORD_SIZE_BASE_SHIFT; @@ -234,8 +224,8 @@ static s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw) /* FWSM register */ mac->has_fwsm = TRUE; /* ARC supported; valid only if manageability features are enabled. */ - mac->arc_subsystem_valid = (E1000_READ_REG(hw, E1000_FWSM) & - E1000_FWSM_MODE_MASK) ? TRUE : FALSE; + mac->arc_subsystem_valid = !!(E1000_READ_REG(hw, E1000_FWSM) & + E1000_FWSM_MODE_MASK); /* Adaptive IFS not supported */ mac->adaptive_ifs = FALSE; @@ -377,14 +367,13 @@ static s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw) ret_val = e1000_acquire_swfw_sync_80003es2lan(hw, E1000_SWFW_EEP_SM); if (ret_val) - goto out; + return ret_val; ret_val = e1000_acquire_nvm_generic(hw); if (ret_val) e1000_release_swfw_sync_80003es2lan(hw, E1000_SWFW_EEP_SM); -out: return ret_val; } @@ -415,23 +404,20 @@ static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask) u32 swfw_sync; u32 swmask = mask; u32 fwmask = mask << 16; - s32 ret_val = E1000_SUCCESS; - s32 i = 0, timeout = 50; + s32 i = 0; + s32 timeout = 50; DEBUGFUNC("e1000_acquire_swfw_sync_80003es2lan"); while (i < timeout) { - if (e1000_get_hw_semaphore_generic(hw)) { - ret_val = -E1000_ERR_SWFW_SYNC; - goto out; - } + if (e1000_get_hw_semaphore_generic(hw)) + return -E1000_ERR_SWFW_SYNC; swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); if (!(swfw_sync & (fwmask | swmask))) break; - /* - * Firmware currently using resource (fwmask) + /* Firmware currently using resource (fwmask) * or other software thread using resource (swmask) */ e1000_put_hw_semaphore_generic(hw); @@ -441,8 +427,7 @@ static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask) if (i == timeout) { DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); - ret_val = -E1000_ERR_SWFW_SYNC; - goto out; + return -E1000_ERR_SWFW_SYNC; } swfw_sync |= swmask; @@ -450,8 +435,7 @@ static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask) e1000_put_hw_semaphore_generic(hw); -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -497,14 +481,13 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, ret_val = e1000_acquire_phy_80003es2lan(hw); if (ret_val) - goto out; + return ret_val; /* Select Configuration Page */ if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { page_select = GG82563_PHY_PAGE_SELECT; } else { - /* - * Use Alternative Page Select register to access + /* Use Alternative Page Select register to access * registers 30 and 31 */ page_select = GG82563_PHY_PAGE_SELECT_ALT; @@ -514,12 +497,11 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, ret_val = e1000_write_phy_reg_mdic(hw, page_select, temp); if (ret_val) { e1000_release_phy_80003es2lan(hw); - goto out; + return ret_val; } - if (hw->dev_spec._80003es2lan.mdic_wa_enable == TRUE) { - /* - * The "ready" bit in the MDIC register may be incorrectly set + if (hw->dev_spec._80003es2lan.mdic_wa_enable) { + /* The "ready" bit in the MDIC register may be incorrectly set * before the device has completed the "Page Select" MDI * transaction. So we wait 200us after each MDI command... */ @@ -529,9 +511,8 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp); if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { - ret_val = -E1000_ERR_PHY; e1000_release_phy_80003es2lan(hw); - goto out; + return -E1000_ERR_PHY; } usec_delay(200); @@ -549,7 +530,6 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, e1000_release_phy_80003es2lan(hw); -out: return ret_val; } @@ -572,14 +552,13 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, ret_val = e1000_acquire_phy_80003es2lan(hw); if (ret_val) - goto out; + return ret_val; /* Select Configuration Page */ if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { page_select = GG82563_PHY_PAGE_SELECT; } else { - /* - * Use Alternative Page Select register to access + /* Use Alternative Page Select register to access * registers 30 and 31 */ page_select = GG82563_PHY_PAGE_SELECT_ALT; @@ -589,12 +568,11 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, ret_val = e1000_write_phy_reg_mdic(hw, page_select, temp); if (ret_val) { e1000_release_phy_80003es2lan(hw); - goto out; + return ret_val; } - if (hw->dev_spec._80003es2lan.mdic_wa_enable == TRUE) { - /* - * The "ready" bit in the MDIC register may be incorrectly set + if (hw->dev_spec._80003es2lan.mdic_wa_enable) { + /* The "ready" bit in the MDIC register may be incorrectly set * before the device has completed the "Page Select" MDI * transaction. So we wait 200us after each MDI command... */ @@ -604,9 +582,8 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp); if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { - ret_val = -E1000_ERR_PHY; e1000_release_phy_80003es2lan(hw); - goto out; + return -E1000_ERR_PHY; } usec_delay(200); @@ -624,7 +601,6 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, e1000_release_phy_80003es2lan(hw); -out: return ret_val; } @@ -655,7 +631,6 @@ static s32 e1000_write_nvm_80003es2lan(struct e1000_hw *hw, u16 offset, static s32 e1000_get_cfg_done_80003es2lan(struct e1000_hw *hw) { s32 timeout = PHY_CFG_TIMEOUT; - s32 ret_val = E1000_SUCCESS; u32 mask = E1000_NVM_CFG_DONE_PORT_0; DEBUGFUNC("e1000_get_cfg_done_80003es2lan"); @@ -671,12 +646,10 @@ static s32 e1000_get_cfg_done_80003es2lan(struct e1000_hw *hw) } if (!timeout) { DEBUGOUT("MNG configuration cycle has not completed.\n"); - ret_val = -E1000_ERR_RESET; - goto out; + return -E1000_ERR_RESET; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -688,33 +661,32 @@ out: **/ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; u16 phy_data; bool link; DEBUGFUNC("e1000_phy_force_speed_duplex_80003es2lan"); if (!(hw->phy.ops.read_reg)) - goto out; + return E1000_SUCCESS; - /* - * Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI + /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI * forced whenever speed and duplex are forced. */ ret_val = hw->phy.ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_AUTO; ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_SPEC_CTRL, phy_data); if (ret_val) - goto out; + return ret_val; DEBUGOUT1("GG82563 PSCR: %X\n", phy_data); ret_val = hw->phy.ops.read_reg(hw, PHY_CONTROL, &phy_data); if (ret_val) - goto out; + return ret_val; e1000_phy_force_speed_duplex_setup(hw, &phy_data); @@ -723,7 +695,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_data); if (ret_val) - goto out; + return ret_val; usec_delay(1); @@ -733,32 +705,30 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) - goto out; + return ret_val; if (!link) { - /* - * We didn't get link. + /* We didn't get link. * Reset the DSP and cross our fingers. */ ret_val = e1000_phy_reset_dsp_generic(hw); if (ret_val) - goto out; + return ret_val; } /* Try once more */ ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) - goto out; + return ret_val; } ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; - /* - * Resetting the phy means we need to verify the TX_CLK corresponds + /* Resetting the phy means we need to verify the TX_CLK corresponds * to the link speed. 10Mbps -> 2.5MHz, else 25MHz. */ phy_data &= ~GG82563_MSCR_TX_CLK_MASK; @@ -767,15 +737,13 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) else phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25; - /* - * In addition, we must re-enable CRS on Tx for both half and full + /* In addition, we must re-enable CRS on Tx for both half and full * duplex. */ phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, phy_data); -out: return ret_val; } @@ -789,32 +757,29 @@ out: static s32 e1000_get_cable_length_80003es2lan(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; u16 phy_data, index; DEBUGFUNC("e1000_get_cable_length_80003es2lan"); if (!(hw->phy.ops.read_reg)) - goto out; + return E1000_SUCCESS; ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_DSP_DISTANCE, &phy_data); if (ret_val) - goto out; + return ret_val; index = phy_data & GG82563_DSPD_CABLE_LENGTH; - if (index >= GG82563_CABLE_LENGTH_TABLE_SIZE - 5) { - ret_val = -E1000_ERR_PHY; - goto out; - } + if (index >= GG82563_CABLE_LENGTH_TABLE_SIZE - 5) + return -E1000_ERR_PHY; phy->min_cable_length = e1000_gg82563_cable_length_table[index]; phy->max_cable_length = e1000_gg82563_cable_length_table[index + 5]; phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -855,11 +820,11 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw) { u32 ctrl; s32 ret_val; + u16 kum_reg_data; DEBUGFUNC("e1000_reset_hw_80003es2lan"); - /* - * Prevent the PCI-E bus from sticking if there is no TLP connection + /* Prevent the PCI-E bus from sticking if there is no TLP connection * on the last TLP read/write transaction when MAC is reset. */ ret_val = e1000_disable_pcie_master_generic(hw); @@ -878,23 +843,30 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw) ctrl = E1000_READ_REG(hw, E1000_CTRL); ret_val = e1000_acquire_phy_80003es2lan(hw); + if (ret_val) + return ret_val; + DEBUGOUT("Issuing a global reset to MAC\n"); E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); e1000_release_phy_80003es2lan(hw); + /* Disable IBIST slave mode (far-end loopback) */ + e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM, + &kum_reg_data); + kum_reg_data |= E1000_KMRNCTRLSTA_IBIST_DISABLE; + e1000_write_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM, + kum_reg_data); + ret_val = e1000_get_auto_rd_done_generic(hw); if (ret_val) /* We don't want to continue accessing MAC registers. */ - goto out; + return ret_val; /* Clear any pending interrupt events. */ E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); E1000_READ_REG(hw, E1000_ICR); - ret_val = e1000_check_alt_mac_addr_generic(hw); - -out: - return ret_val; + return e1000_check_alt_mac_addr_generic(hw); } /** @@ -917,9 +889,9 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw) /* Initialize identification LED */ ret_val = mac->ops.id_led_init(hw); + /* An error is not fatal and we should not stop init due to this */ if (ret_val) DEBUGOUT("Error initializing identification LED\n"); - /* This is not fatal and we should not stop init due to this */ /* Disabling VLAN filtering */ DEBUGOUT("Initializing the IEEE VLAN\n"); @@ -935,6 +907,8 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw) /* Setup link and flow control */ ret_val = mac->ops.setup_link(hw); + if (ret_val) + return ret_val; /* Disable IBIST slave mode (far-end loopback) */ e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM, @@ -945,14 +919,14 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw) /* Set the transmit descriptor write-back policy */ reg_data = E1000_READ_REG(hw, E1000_TXDCTL(0)); - reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) | - E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC; + reg_data = ((reg_data & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC); E1000_WRITE_REG(hw, E1000_TXDCTL(0), reg_data); /* ...for both queues. */ reg_data = E1000_READ_REG(hw, E1000_TXDCTL(1)); - reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) | - E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC; + reg_data = ((reg_data & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC); E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg_data); /* Enable retransmit on late collisions */ @@ -979,18 +953,16 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw) /* default to TRUE to enable the MDIC W/A */ hw->dev_spec._80003es2lan.mdic_wa_enable = TRUE; - ret_val = e1000_read_kmrn_reg_80003es2lan(hw, - E1000_KMRNCTRLSTA_OFFSET >> - E1000_KMRNCTRLSTA_OFFSET_SHIFT, - &i); + ret_val = + e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_OFFSET >> + E1000_KMRNCTRLSTA_OFFSET_SHIFT, &i); if (!ret_val) { if ((i & E1000_KMRNCTRLSTA_OPMODE_MASK) == E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO) hw->dev_spec._80003es2lan.mdic_wa_enable = FALSE; } - /* - * Clear all of the statistics registers (clear on read). It is + /* Clear all of the statistics registers (clear on read). It is * important that we do this after we have tried to establish link * because the symbol error count will increment wildly if there * is no link. @@ -1037,6 +1009,13 @@ static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw) reg |= (1 << 28); E1000_WRITE_REG(hw, E1000_TARC(1), reg); + /* Disable IPv6 extension header parsing because some malformed + * IPv6 headers can hang the Rx. + */ + reg = E1000_READ_REG(hw, E1000_RFCTL); + reg |= (E1000_RFCTL_IPV6_EX_DIS | E1000_RFCTL_NEW_IPV6_EXT_DIS); + E1000_WRITE_REG(hw, E1000_RFCTL, reg); + return; } @@ -1050,14 +1029,14 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; s32 ret_val; - u32 ctrl_ext; + u32 reg; u16 data; DEBUGFUNC("e1000_copper_link_setup_gg82563_80003es2lan"); ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, &data); if (ret_val) - goto out; + return ret_val; data |= GG82563_MSCR_ASSERT_CRS_ON_TX; /* Use 25MHz for both link down and 1000Base-T for Tx clock. */ @@ -1065,10 +1044,9 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, data); if (ret_val) - goto out; + return ret_val; - /* - * Options: + /* Options: * MDI/MDI-X = 0 (default) * 0 - Auto for all speeds * 1 - MDI mode @@ -1077,7 +1055,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) */ ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_SPEC_CTRL, &data); if (ret_val) - goto out; + return ret_val; data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK; @@ -1094,8 +1072,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) break; } - /* - * Options: + /* Options: * disable_polarity_correction = 0 (default) * Automatic Correction for Reversed Cable Polarity * 0 - Disabled @@ -1107,90 +1084,86 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_SPEC_CTRL, data); if (ret_val) - goto out; + return ret_val; /* SW Reset the PHY so all changes take effect */ ret_val = hw->phy.ops.commit(hw); if (ret_val) { DEBUGOUT("Error Resetting the PHY\n"); - goto out; + return ret_val; } /* Bypass Rx and Tx FIFO's */ - ret_val = e1000_write_kmrn_reg_80003es2lan(hw, - E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL, - E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS | - E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS); + reg = E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL; + data = (E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS | + E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS); + ret_val = e1000_write_kmrn_reg_80003es2lan(hw, reg, data); if (ret_val) - goto out; + return ret_val; - ret_val = e1000_read_kmrn_reg_80003es2lan(hw, - E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE, &data); + reg = E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE; + ret_val = e1000_read_kmrn_reg_80003es2lan(hw, reg, &data); if (ret_val) - goto out; + return ret_val; data |= E1000_KMRNCTRLSTA_OPMODE_E_IDLE; - ret_val = e1000_write_kmrn_reg_80003es2lan(hw, - E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE, data); + ret_val = e1000_write_kmrn_reg_80003es2lan(hw, reg, data); if (ret_val) - goto out; + return ret_val; ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_SPEC_CTRL_2, &data); if (ret_val) - goto out; + return ret_val; data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG; ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_SPEC_CTRL_2, data); if (ret_val) - goto out; + return ret_val; - ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); - ctrl_ext &= ~(E1000_CTRL_EXT_LINK_MODE_MASK); - E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + reg = E1000_READ_REG(hw, E1000_CTRL_EXT); + reg &= ~E1000_CTRL_EXT_LINK_MODE_MASK; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg); ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, &data); if (ret_val) - goto out; + return ret_val; - /* - * Do not init these registers when the HW is in IAMT mode, since the + /* Do not init these registers when the HW is in IAMT mode, since the * firmware will have already initialized them. We only initialize * them if the HW is not in IAMT mode. */ - if (!(hw->mac.ops.check_mng_mode(hw))) { + if (!hw->mac.ops.check_mng_mode(hw)) { /* Enable Electrical Idle on the PHY */ data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE; ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, data); if (ret_val) - goto out; + return ret_val; ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, &data); if (ret_val) - goto out; + return ret_val; data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, data); if (ret_val) - goto out; + return ret_val; } - /* - * Workaround: Disable padding in Kumeran interface in the MAC + /* Workaround: Disable padding in Kumeran interface in the MAC * and in the PHY to avoid CRC errors. */ ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_INBAND_CTRL, &data); if (ret_val) - goto out; + return ret_val; data |= GG82563_ICR_DIS_PADDING; ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_INBAND_CTRL, data); if (ret_val) - goto out; + return ret_val; -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1213,42 +1186,42 @@ static s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw) ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); E1000_WRITE_REG(hw, E1000_CTRL, ctrl); - /* - * Set the mac to wait the maximum time between each + /* Set the mac to wait the maximum time between each * iteration and increase the max iterations when * polling the phy; this fixes erroneous timeouts at 10Mbps. */ ret_val = e1000_write_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 4), 0xFFFF); if (ret_val) - goto out; + return ret_val; ret_val = e1000_read_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 9), ®_data); if (ret_val) - goto out; + return ret_val; reg_data |= 0x3F; ret_val = e1000_write_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 9), reg_data); if (ret_val) - goto out; - ret_val = e1000_read_kmrn_reg_80003es2lan(hw, - E1000_KMRNCTRLSTA_OFFSET_INB_CTRL, ®_data); + return ret_val; + ret_val = + e1000_read_kmrn_reg_80003es2lan(hw, + E1000_KMRNCTRLSTA_OFFSET_INB_CTRL, + ®_data); if (ret_val) - goto out; + return ret_val; reg_data |= E1000_KMRNCTRLSTA_INB_CTRL_DIS_PADDING; - ret_val = e1000_write_kmrn_reg_80003es2lan(hw, - E1000_KMRNCTRLSTA_OFFSET_INB_CTRL, reg_data); + ret_val = + e1000_write_kmrn_reg_80003es2lan(hw, + E1000_KMRNCTRLSTA_OFFSET_INB_CTRL, + reg_data); if (ret_val) - goto out; + return ret_val; ret_val = e1000_copper_link_setup_gg82563_80003es2lan(hw); if (ret_val) - goto out; + return ret_val; - ret_val = e1000_setup_copper_link_generic(hw); - -out: - return ret_val; + return e1000_setup_copper_link_generic(hw); } /** @@ -1271,7 +1244,7 @@ static s32 e1000_cfg_on_link_up_80003es2lan(struct e1000_hw *hw) ret_val = e1000_get_speed_and_duplex_copper_generic(hw, &speed, &duplex); if (ret_val) - goto out; + return ret_val; if (speed == SPEED_1000) ret_val = e1000_cfg_kmrn_1000_80003es2lan(hw); @@ -1279,7 +1252,6 @@ static s32 e1000_cfg_on_link_up_80003es2lan(struct e1000_hw *hw) ret_val = e1000_cfg_kmrn_10_100_80003es2lan(hw, duplex); } -out: return ret_val; } @@ -1293,7 +1265,7 @@ out: **/ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; u32 tipg; u32 i = 0; u16 reg_data, reg_data2; @@ -1301,11 +1273,12 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex) DEBUGFUNC("e1000_configure_kmrn_for_10_100"); reg_data = E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT; - ret_val = e1000_write_kmrn_reg_80003es2lan(hw, - E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, - reg_data); + ret_val = + e1000_write_kmrn_reg_80003es2lan(hw, + E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, + reg_data); if (ret_val) - goto out; + return ret_val; /* Configure Transmit Inter-Packet Gap */ tipg = E1000_READ_REG(hw, E1000_TIPG); @@ -1317,12 +1290,12 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex) ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); if (ret_val) - goto out; + return ret_val; ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data2); if (ret_val) - goto out; + return ret_val; i++; } while ((reg_data != reg_data2) && (i < GG82563_MAX_KMRN_RETRY)); @@ -1331,11 +1304,7 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex) else reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; - ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, - reg_data); - -out: - return ret_val; + return hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); } /** @@ -1347,7 +1316,7 @@ out: **/ static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; u16 reg_data, reg_data2; u32 tipg; u32 i = 0; @@ -1355,10 +1324,12 @@ static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw) DEBUGFUNC("e1000_configure_kmrn_for_1000"); reg_data = E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT; - ret_val = e1000_write_kmrn_reg_80003es2lan(hw, - E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, reg_data); + ret_val = + e1000_write_kmrn_reg_80003es2lan(hw, + E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, + reg_data); if (ret_val) - goto out; + return ret_val; /* Configure Transmit Inter-Packet Gap */ tipg = E1000_READ_REG(hw, E1000_TIPG); @@ -1370,21 +1341,18 @@ static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw) ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); if (ret_val) - goto out; + return ret_val; ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data2); if (ret_val) - goto out; + return ret_val; i++; } while ((reg_data != reg_data2) && (i < GG82563_MAX_KMRN_RETRY)); reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; - ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, - reg_data); -out: - return ret_val; + return hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); } /** @@ -1401,13 +1369,13 @@ static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, u16 *data) { u32 kmrnctrlsta; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_read_kmrn_reg_80003es2lan"); ret_val = e1000_acquire_mac_csr_80003es2lan(hw); if (ret_val) - goto out; + return ret_val; kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN; @@ -1421,7 +1389,6 @@ static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, e1000_release_mac_csr_80003es2lan(hw); -out: return ret_val; } @@ -1439,13 +1406,13 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, u16 data) { u32 kmrnctrlsta; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_write_kmrn_reg_80003es2lan"); ret_val = e1000_acquire_mac_csr_80003es2lan(hw); if (ret_val) - goto out; + return ret_val; kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & E1000_KMRNCTRLSTA_OFFSET) | data; @@ -1456,7 +1423,6 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, e1000_release_mac_csr_80003es2lan(hw); -out: return ret_val; } @@ -1466,23 +1432,19 @@ out: **/ static s32 e1000_read_mac_addr_80003es2lan(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_read_mac_addr_80003es2lan"); - /* - * If there's an alternate MAC address place it in RAR0 + /* If there's an alternate MAC address place it in RAR0 * so that it will override the Si installed default perm * address. */ ret_val = e1000_check_alt_mac_addr_generic(hw); if (ret_val) - goto out; + return ret_val; - ret_val = e1000_read_mac_addr_generic(hw); - -out: - return ret_val; + return e1000_read_mac_addr_generic(hw); } /** diff --git a/sys/dev/e1000/e1000_80003es2lan.h b/sys/dev/e1000/e1000_80003es2lan.h index 157468e485d..3807e46305d 100644 --- a/sys/dev/e1000/e1000_80003es2lan.h +++ b/sys/dev/e1000/e1000_80003es2lan.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2013, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -51,34 +51,32 @@ #define E1000_KMRNCTRLSTA_OPMODE_MASK 0x000C #define E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO 0x0004 -#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ +#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gig Carry Extend Padding */ #define DEFAULT_TCTL_EXT_GCEX_80003ES2LAN 0x00010000 #define DEFAULT_TIPG_IPGT_1000_80003ES2LAN 0x8 #define DEFAULT_TIPG_IPGT_10_100_80003ES2LAN 0x9 /* GG82563 PHY Specific Status Register (Page 0, Register 16 */ -#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Reversal Disabled */ +#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Reversal Dis */ #define GG82563_PSCR_CROSSOVER_MODE_MASK 0x0060 #define GG82563_PSCR_CROSSOVER_MODE_MDI 0x0000 /* 00=Manual MDI */ #define GG82563_PSCR_CROSSOVER_MODE_MDIX 0x0020 /* 01=Manual MDIX */ #define GG82563_PSCR_CROSSOVER_MODE_AUTO 0x0060 /* 11=Auto crossover */ /* PHY Specific Control Register 2 (Page 0, Register 26) */ -#define GG82563_PSCR2_REVERSE_AUTO_NEG 0x2000 /* 1=Reverse Auto-Nego */ +#define GG82563_PSCR2_REVERSE_AUTO_NEG 0x2000 /* 1=Reverse Auto-Neg */ /* MAC Specific Control Register (Page 2, Register 21) */ /* Tx clock speed for Link Down and 1000BASE-T for the following speeds */ #define GG82563_MSCR_TX_CLK_MASK 0x0007 #define GG82563_MSCR_TX_CLK_10MBPS_2_5 0x0004 #define GG82563_MSCR_TX_CLK_100MBPS_25 0x0005 -#define GG82563_MSCR_TX_CLK_1000MBPS_2_5 0x0006 #define GG82563_MSCR_TX_CLK_1000MBPS_25 0x0007 #define GG82563_MSCR_ASSERT_CRS_ON_TX 0x0010 /* 1=Assert */ -/* DSP Distance Register (Page 5, Register 26) */ -/* +/* DSP Distance Register (Page 5, Register 26) * 0 = <50M * 1 = 50-80M * 2 = 80-100M diff --git a/sys/dev/e1000/e1000_82571.c b/sys/dev/e1000/e1000_82571.c index ca7a1d021f7..2c985900834 100644 --- a/sys/dev/e1000/e1000_82571.c +++ b/sys/dev/e1000/e1000_82571.c @@ -927,9 +927,9 @@ static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset, } for (i = 0; i < words; i++) { - eewr = (data[i] << E1000_NVM_RW_REG_DATA) | - ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) | - E1000_NVM_RW_REG_START; + eewr = ((data[i] << E1000_NVM_RW_REG_DATA) | + ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) | + E1000_NVM_RW_REG_START); ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_WRITE); if (ret_val) @@ -1101,8 +1101,6 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) default: break; } - if (ret_val) - DEBUGOUT("Cannot acquire MDIO ownership\n"); ctrl = E1000_READ_REG(hw, E1000_CTRL); @@ -1111,9 +1109,16 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) /* Must release MDIO ownership and mutex after MAC reset. */ switch (hw->mac.type) { + case e1000_82573: + /* Release mutex only if the hw semaphore is acquired */ + if (!ret_val) + e1000_put_hw_semaphore_82573(hw); + break; case e1000_82574: case e1000_82583: - e1000_put_hw_semaphore_82574(hw); + /* Release mutex only if the hw semaphore is acquired */ + if (!ret_val) + e1000_put_hw_semaphore_82574(hw); break; default: break; @@ -1222,8 +1227,8 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw) /* Set the transmit descriptor write-back policy */ reg_data = E1000_READ_REG(hw, E1000_TXDCTL(0)); - reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) | - E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC; + reg_data = ((reg_data & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC); E1000_WRITE_REG(hw, E1000_TXDCTL(0), reg_data); /* ...for both queues. */ @@ -1239,9 +1244,9 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw) break; default: reg_data = E1000_READ_REG(hw, E1000_TXDCTL(1)); - reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) | - E1000_TXDCTL_FULL_TX_DESC_WB | - E1000_TXDCTL_COUNT_DESC; + reg_data = ((reg_data & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB | + E1000_TXDCTL_COUNT_DESC); E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg_data); break; } diff --git a/sys/dev/e1000/e1000_82575.c b/sys/dev/e1000/e1000_82575.c index 5722f4688ca..1707236e097 100644 --- a/sys/dev/e1000/e1000_82575.c +++ b/sys/dev/e1000/e1000_82575.c @@ -52,6 +52,7 @@ static void e1000_release_phy_82575(struct e1000_hw *hw); static s32 e1000_acquire_nvm_82575(struct e1000_hw *hw); static void e1000_release_nvm_82575(struct e1000_hw *hw); static s32 e1000_check_for_link_82575(struct e1000_hw *hw); +static s32 e1000_check_for_link_media_swap(struct e1000_hw *hw); static s32 e1000_get_cfg_done_82575(struct e1000_hw *hw); static s32 e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed, u16 *duplex); @@ -144,6 +145,7 @@ static bool e1000_sgmii_uses_mdio_82575(struct e1000_hw *hw) break; case e1000_82580: case e1000_i350: + case e1000_i354: case e1000_i210: case e1000_i211: reg = E1000_READ_REG(hw, E1000_MDICNFG); @@ -207,6 +209,7 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_82580: case e1000_i350: + case e1000_i354: phy->ops.read_reg = e1000_read_phy_reg_82580; phy->ops.write_reg = e1000_write_phy_reg_82580; break; @@ -226,6 +229,8 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw) /* Verify phy id and set remaining function pointers */ switch (phy->id) { + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: case I347AT4_E_PHY_ID: case M88E1112_E_PHY_ID: case M88E1340M_E_PHY_ID: @@ -238,9 +243,36 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw) phy->id == M88E1340M_E_PHY_ID) phy->ops.get_cable_length = e1000_get_cable_length_m88_gen2; + else if (phy->id == M88E1543_E_PHY_ID || + phy->id == M88E1512_E_PHY_ID) + phy->ops.get_cable_length = + e1000_get_cable_length_m88_gen2; else phy->ops.get_cable_length = e1000_get_cable_length_m88; phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88; + /* Check if this PHY is confgured for media swap. */ + if (phy->id == M88E1112_E_PHY_ID) { + u16 data; + + ret_val = phy->ops.write_reg(hw, + E1000_M88E1112_PAGE_ADDR, + 2); + if (ret_val) + goto out; + + ret_val = phy->ops.read_reg(hw, + E1000_M88E1112_MAC_CTRL_1, + &data); + if (ret_val) + goto out; + + data = (data & E1000_M88E1112_MAC_CTRL_1_MODE_MASK) >> + E1000_M88E1112_MAC_CTRL_1_MODE_SHIFT; + if (data == E1000_M88E1112_AUTO_COPPER_SGMII || + data == E1000_M88E1112_AUTO_COPPER_BASEX) + hw->mac.ops.check_for_link = + e1000_check_for_link_media_swap; + } break; case IGP03E1000_E_PHY_ID: case IGP04E1000_E_PHY_ID: @@ -355,6 +387,7 @@ s32 e1000_init_nvm_params_82575(struct e1000_hw *hw) nvm->ops.update = e1000_update_nvm_checksum_82580; break; case e1000_i350: + case e1000_i354: nvm->ops.validate = e1000_validate_nvm_checksum_i350; nvm->ops.update = e1000_update_nvm_checksum_i350; break; @@ -388,7 +421,7 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw) mac->rar_entry_count = E1000_RAR_ENTRIES_82576; if (mac->type == e1000_82580) mac->rar_entry_count = E1000_RAR_ENTRIES_82580; - if (mac->type == e1000_i350) + if (mac->type == e1000_i350 || mac->type == e1000_i354) mac->rar_entry_count = E1000_RAR_ENTRIES_I350; /* Enable EEE default settings for EEE supported devices */ @@ -436,7 +469,7 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw) mac->ops.config_collision_dist = e1000_config_collision_dist_82575; /* multicast address update */ mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic; - if (mac->type == e1000_i350) { + if (hw->mac.type == e1000_i350 || mac->type == e1000_i354) { /* writing VFTA */ mac->ops.write_vfta = e1000_write_vfta_i350; /* clearing VFTA */ @@ -622,6 +655,10 @@ static s32 e1000_get_phy_id_82575(struct e1000_hw *hw) DEBUGFUNC("e1000_get_phy_id_82575"); + /* some i354 devices need an extra read for phy id */ + if (hw->mac.type == e1000_i354) + e1000_get_phy_id(hw); + /* * For SGMII PHYs, we try the list of possible addresses until * we find one that works. For non-SGMII PHYs @@ -645,6 +682,7 @@ static s32 e1000_get_phy_id_82575(struct e1000_hw *hw) break; case e1000_82580: case e1000_i350: + case e1000_i354: case e1000_i210: case e1000_i211: mdic = E1000_READ_REG(hw, E1000_MDICNFG); @@ -1172,6 +1210,61 @@ static s32 e1000_check_for_link_82575(struct e1000_hw *hw) return ret_val; } +/** + * e1000_check_for_link_media_swap - Check which M88E1112 interface linked + * @hw: pointer to the HW structure + * + * Poll the M88E1112 interfaces to see which interface achieved link. + */ +static s32 e1000_check_for_link_media_swap(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + u8 port = 0; + + DEBUGFUNC("e1000_check_for_link_media_swap"); + + /* Check the copper medium. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 0); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1112_STATUS, &data); + if (ret_val) + return ret_val; + + if (data & E1000_M88E1112_STATUS_LINK) + port = E1000_MEDIA_PORT_COPPER; + + /* Check the other medium. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 1); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1112_STATUS, &data); + if (ret_val) + return ret_val; + + /* reset page to 0 */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 0); + if (ret_val) + return ret_val; + + if (data & E1000_M88E1112_STATUS_LINK) + port = E1000_MEDIA_PORT_OTHER; + + /* Determine if a swap needs to happen. */ + if (port && (hw->dev_spec._82575.media_port != port)) { + hw->dev_spec._82575.media_port = port; + hw->dev_spec._82575.media_changed = TRUE; + } else { + ret_val = e1000_check_for_link_82575(hw); + } + + return E1000_SUCCESS; +} + /** * e1000_power_up_serdes_link_82575 - Power up the serdes link after shutdown * @hw: pointer to the HW structure @@ -1215,6 +1308,7 @@ static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, { struct e1000_mac_info *mac = &hw->mac; u32 pcs; + u32 status; DEBUGFUNC("e1000_get_pcs_speed_and_duplex_82575"); @@ -1245,6 +1339,18 @@ static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, else *duplex = HALF_DUPLEX; + /* Check if it is an I354 2.5Gb backplane connection. */ + if (mac->type == e1000_i354) { + status = E1000_READ_REG(hw, E1000_STATUS); + if ((status & E1000_STATUS_2P5_SKU) && + !(status & E1000_STATUS_2P5_SKU_OVER)) { + *speed = SPEED_2500; + *duplex = FULL_DUPLEX; + DEBUGOUT("2500 Mbs, "); + DEBUGOUT("Full Duplex\n"); + } + } + } else { mac->serdes_has_link = FALSE; *speed = 0; @@ -1430,11 +1536,18 @@ static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw) ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); E1000_WRITE_REG(hw, E1000_CTRL, ctrl); - /* Clear Go Link Disconnect bit */ - if (hw->mac.type >= e1000_82580) { + /* Clear Go Link Disconnect bit on supported devices */ + switch (hw->mac.type) { + case e1000_82580: + case e1000_i350: + case e1000_i210: + case e1000_i211: phpm_reg = E1000_READ_REG(hw, E1000_82580_PHY_POWER_MGMT); phpm_reg &= ~E1000_82580_PM_GO_LINKD; E1000_WRITE_REG(hw, E1000_82580_PHY_POWER_MGMT, phpm_reg); + break; + default: + break; } ret_val = e1000_setup_serdes_link_82575(hw); @@ -1458,6 +1571,8 @@ static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw) case I347AT4_E_PHY_ID: case M88E1112_E_PHY_ID: case M88E1340M_E_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: case I210_I_PHY_ID: ret_val = e1000_copper_link_setup_m88_gen2(hw); break; @@ -2132,42 +2247,33 @@ out: **/ void e1000_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf) { - u32 dtxswc; + u32 reg_val, reg_offset; switch (hw->mac.type) { case e1000_82576: - dtxswc = E1000_READ_REG(hw, E1000_DTXSWC); - if (enable) { - dtxswc |= (E1000_DTXSWC_MAC_SPOOF_MASK | - E1000_DTXSWC_VLAN_SPOOF_MASK); - /* The PF can spoof - it has to in order to - * support emulation mode NICs */ - dtxswc ^= (1 << pf | 1 << (pf + - E1000_DTXSWC_VLAN_SPOOF_SHIFT)); - } else { - dtxswc &= ~(E1000_DTXSWC_MAC_SPOOF_MASK | - E1000_DTXSWC_VLAN_SPOOF_MASK); - } - E1000_WRITE_REG(hw, E1000_DTXSWC, dtxswc); + reg_offset = E1000_DTXSWC; break; case e1000_i350: - dtxswc = E1000_READ_REG(hw, E1000_TXSWC); - if (enable) { - dtxswc |= (E1000_DTXSWC_MAC_SPOOF_MASK | - E1000_DTXSWC_VLAN_SPOOF_MASK); - /* The PF can spoof - it has to in order to - * support emulation mode NICs - */ - dtxswc ^= (1 << pf | 1 << (pf + - E1000_DTXSWC_VLAN_SPOOF_SHIFT)); - } else { - dtxswc &= ~(E1000_DTXSWC_MAC_SPOOF_MASK | - E1000_DTXSWC_VLAN_SPOOF_MASK); - } - E1000_WRITE_REG(hw, E1000_TXSWC, dtxswc); - default: + case e1000_i354: + reg_offset = E1000_TXSWC; break; + default: + return; } + + reg_val = E1000_READ_REG(hw, reg_offset); + if (enable) { + reg_val |= (E1000_DTXSWC_MAC_SPOOF_MASK | + E1000_DTXSWC_VLAN_SPOOF_MASK); + /* The PF can spoof - it has to in order to + * support emulation mode NICs + */ + reg_val ^= (1 << pf | 1 << (pf + MAX_NUM_VFS)); + } else { + reg_val &= ~(E1000_DTXSWC_MAC_SPOOF_MASK | + E1000_DTXSWC_VLAN_SPOOF_MASK); + } + E1000_WRITE_REG(hw, reg_offset, reg_val); } /** @@ -2191,6 +2297,7 @@ void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable) E1000_WRITE_REG(hw, E1000_DTXSWC, dtxswc); break; case e1000_i350: + case e1000_i354: dtxswc = E1000_READ_REG(hw, E1000_TXSWC); if (enable) dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN; @@ -2387,10 +2494,6 @@ static s32 e1000_reset_hw_82580(struct e1000_hw *hw) DEBUGOUT("Auto Read Done did not complete\n"); } - /* If EEPROM is not present, run manual init scripts */ - if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES)) - e1000_reset_init_script_82575(hw); - /* clear global device reset status bit */ E1000_WRITE_REG(hw, E1000_STATUS, E1000_STAT_DEV_RST_SET); @@ -2644,6 +2747,45 @@ out: return ret_val; } +/** + * __e1000_access_emi_reg - Read/write EMI register + * @hw: pointer to the HW structure + * @addr: EMI address to program + * @data: pointer to value to read/write from/to the EMI address + * @read: boolean flag to indicate read or write + **/ +static s32 __e1000_access_emi_reg(struct e1000_hw *hw, u16 address, + u16 *data, bool read) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("__e1000_access_emi_reg"); + + ret_val = hw->phy.ops.write_reg(hw, E1000_EMIADD, address); + if (ret_val) + return ret_val; + + if (read) + ret_val = hw->phy.ops.read_reg(hw, E1000_EMIDATA, data); + else + ret_val = hw->phy.ops.write_reg(hw, E1000_EMIDATA, *data); + + return ret_val; +} + +/** + * e1000_read_emi_reg - Read Extended Management Interface register + * @hw: pointer to the HW structure + * @addr: EMI address to program + * @data: value to be read from the EMI address + **/ +s32 e1000_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data) +{ + DEBUGFUNC("e1000_read_emi_reg"); + + return __e1000_access_emi_reg(hw, addr, data, TRUE); +} + /** * e1000_set_eee_i350 - Enable/disable EEE support * @hw: pointer to the HW structure @@ -2689,6 +2831,114 @@ out: return ret_val; } +/** + * e1000_set_eee_i354 - Enable/disable EEE support + * @hw: pointer to the HW structure + * + * Enable/disable EEE legacy mode based on setting in dev_spec structure. + * + **/ +s32 e1000_set_eee_i354(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_data; + + DEBUGFUNC("e1000_set_eee_i354"); + + if ((hw->phy.media_type != e1000_media_type_copper) || + ((phy->id != M88E1543_E_PHY_ID) && + (phy->id != M88E1512_E_PHY_ID))) + goto out; + + if (!hw->dev_spec._82575.eee_disable) { + /* Switch to PHY page 18. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 18); + if (ret_val) + goto out; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1543_EEE_CTRL_1, + &phy_data); + if (ret_val) + goto out; + + phy_data |= E1000_M88E1543_EEE_CTRL_1_MS; + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_EEE_CTRL_1, + phy_data); + if (ret_val) + goto out; + + /* Return the PHY to page 0. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0); + if (ret_val) + goto out; + + /* Turn on EEE advertisement. */ + ret_val = e1000_read_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + phy_data |= E1000_EEE_ADV_100_SUPPORTED | + E1000_EEE_ADV_1000_SUPPORTED; + ret_val = e1000_write_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + phy_data); + } else { + /* Turn off EEE advertisement. */ + ret_val = e1000_read_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + phy_data &= ~(E1000_EEE_ADV_100_SUPPORTED | + E1000_EEE_ADV_1000_SUPPORTED); + ret_val = e1000_write_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + phy_data); + } + +out: + return ret_val; +} + +/** + * e1000_get_eee_status_i354 - Get EEE status + * @hw: pointer to the HW structure + * @status: EEE status + * + * Get EEE status by guessing based on whether Tx or Rx LPI indications have + * been received. + **/ +s32 e1000_get_eee_status_i354(struct e1000_hw *hw, bool *status) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_data; + + DEBUGFUNC("e1000_get_eee_status_i354"); + + /* Check if EEE is supported on this device. */ + if ((hw->phy.media_type != e1000_media_type_copper) || + ((phy->id != M88E1543_E_PHY_ID) && + (phy->id != M88E1512_E_PHY_ID))) + goto out; + + ret_val = e1000_read_xmdio_reg(hw, E1000_PCS_STATUS_ADDR_I354, + E1000_PCS_STATUS_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + *status = phy_data & (E1000_PCS_STATUS_TX_LPI_RCVD | + E1000_PCS_STATUS_RX_LPI_RCVD) ? TRUE : FALSE; + +out: + return ret_val; +} + /* Due to a hw errata, if the host tries to configure the VFTA register * while performing queries from the BMC or DMA, then the VFTA in some * cases won't be written. @@ -3286,4 +3536,3 @@ void e1000_i2c_bus_clear(struct e1000_hw *hw) e1000_i2c_stop(hw); } - diff --git a/sys/dev/e1000/e1000_82575.h b/sys/dev/e1000/e1000_82575.h index c6bbe186c0d..e7039d8fa20 100644 --- a/sys/dev/e1000/e1000_82575.h +++ b/sys/dev/e1000/e1000_82575.h @@ -245,6 +245,8 @@ union e1000_adv_rx_desc { #define E1000_RXDADV_RSSTYPE_IPV6_UDP_EX 0x00000009 /* RSS Packet Types as indicated in the receive descriptor */ +#define E1000_RXDADV_PKTTYPE_ILMASK 0x000000F0 +#define E1000_RXDADV_PKTTYPE_TLMASK 0x00000F00 #define E1000_RXDADV_PKTTYPE_NONE 0x00000000 #define E1000_RXDADV_PKTTYPE_IPV4 0x00000010 /* IPV4 hdr present */ #define E1000_RXDADV_PKTTYPE_IPV4_EX 0x00000020 /* IPV4 hdr + extensions */ @@ -491,7 +493,10 @@ void e1000_vfta_set_vf(struct e1000_hw *, u16, bool); void e1000_rlpml_set_vf(struct e1000_hw *, u16); s32 e1000_promisc_set_vf(struct e1000_hw *, enum e1000_promisc_type type); u16 e1000_rxpbs_adjust_82580(u32 data); +s32 e1000_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data); s32 e1000_set_eee_i350(struct e1000_hw *); +s32 e1000_set_eee_i354(struct e1000_hw *); +s32 e1000_get_eee_status_i354(struct e1000_hw *, bool *); /* I2C SDA and SCL timing parameters for standard mode */ #define E1000_I2C_T_HD_STA 4 diff --git a/sys/dev/e1000/e1000_api.c b/sys/dev/e1000/e1000_api.c index 158191304ba..c1ca5fd51fb 100644 --- a/sys/dev/e1000/e1000_api.c +++ b/sys/dev/e1000/e1000_api.c @@ -329,9 +329,8 @@ s32 e1000_set_mac_type(struct e1000_hw *hw) case E1000_DEV_ID_I350_DA4: mac->type = e1000_i350; break; -#if defined(QV_RELEASE) && defined(SPRINGVILLE_FLASHLESS_HW) - case E1000_DEV_ID_I210_NVMLESS: -#endif /* QV_RELEASE && SPRINGVILLE_FLASHLESS_HW */ + case E1000_DEV_ID_I210_COPPER_FLASHLESS: + case E1000_DEV_ID_I210_SERDES_FLASHLESS: case E1000_DEV_ID_I210_COPPER: case E1000_DEV_ID_I210_COPPER_OEM1: case E1000_DEV_ID_I210_COPPER_IT: @@ -352,6 +351,11 @@ s32 e1000_set_mac_type(struct e1000_hw *hw) mac->type = e1000_vfadapt_i350; break; + case E1000_DEV_ID_I354_BACKPLANE_1GBPS: + case E1000_DEV_ID_I354_SGMII: + case E1000_DEV_ID_I354_BACKPLANE_2_5GBPS: + mac->type = e1000_i354; + break; default: /* Should never have loaded on this device */ ret_val = -E1000_ERR_MAC_INIT; @@ -447,6 +451,7 @@ s32 e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device) case e1000_82576: case e1000_82580: case e1000_i350: + case e1000_i354: e1000_init_function_pointers_82575(hw); break; case e1000_i210: diff --git a/sys/dev/e1000/e1000_defines.h b/sys/dev/e1000/e1000_defines.h index 48c04b013db..d4b2e561d2f 100644 --- a/sys/dev/e1000/e1000_defines.h +++ b/sys/dev/e1000/e1000_defines.h @@ -43,6 +43,8 @@ /* Wake Up Control */ #define E1000_WUC_APME 0x00000001 /* APM Enable */ #define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ #define E1000_WUC_PHY_WAKE 0x00000100 /* if PHY supports wakeup */ /* Wake Up Filter Control */ @@ -287,7 +289,10 @@ #define E1000_CONNSW_ENRGSRC 0x4 #define E1000_CONNSW_PHYSD 0x400 +#define E1000_CONNSW_PHY_PDN 0x800 #define E1000_CONNSW_SERDESD 0x200 +#define E1000_CONNSW_AUTOSENSE_CONF 0x2 +#define E1000_CONNSW_AUTOSENSE_EN 0x1 #define E1000_PCS_CFG_PCS_EN 8 #define E1000_PCS_LCTL_FLV_LINK_UP 1 #define E1000_PCS_LCTL_FSV_10 0 @@ -325,6 +330,8 @@ #define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Master request status */ #define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ #define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ +#define E1000_STATUS_2P5_SKU 0x00001000 /* Val of 2.5GBE SKU strap */ +#define E1000_STATUS_2P5_SKU_OVER 0x00002000 /* Val of 2.5GBE SKU Over */ #define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ #define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ @@ -336,6 +343,7 @@ #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 +#define SPEED_2500 2500 #define HALF_DUPLEX 1 #define FULL_DUPLEX 2 @@ -650,6 +658,7 @@ #define E1000_EITR_ITR_INT_MASK 0x0000FFFF /* E1000_EITR_CNT_IGNR is only for 82576 and newer */ #define E1000_EITR_CNT_IGNR 0x80000000 /* Don't reset counters on write */ +#define E1000_EITR_INTERVAL 0x00007FFC /* Transmit Descriptor Control */ #define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ @@ -805,6 +814,17 @@ #define E1000_MDICNFG_PHY_MASK 0x03E00000 #define E1000_MDICNFG_PHY_SHIFT 21 +#define E1000_MEDIA_PORT_COPPER 1 +#define E1000_MEDIA_PORT_OTHER 2 +#define E1000_M88E1112_AUTO_COPPER_SGMII 0x2 +#define E1000_M88E1112_AUTO_COPPER_BASEX 0x3 +#define E1000_M88E1112_STATUS_LINK 0x0004 /* Interface Link Bit */ +#define E1000_M88E1112_MAC_CTRL_1 0x10 +#define E1000_M88E1112_MAC_CTRL_1_MODE_MASK 0x0380 /* Mode Select */ +#define E1000_M88E1112_MAC_CTRL_1_MODE_SHIFT 7 +#define E1000_M88E1112_PAGE_ADDR 0x16 +#define E1000_M88E1112_STATUS 0x01 + #define E1000_THSTAT_LOW_EVENT 0x20000000 /* Low thermal threshold */ #define E1000_THSTAT_MID_EVENT 0x00200000 /* Mid thermal threshold */ #define E1000_THSTAT_HIGH_EVENT 0x00002000 /* High thermal threshold */ @@ -821,7 +841,21 @@ #define E1000_EEER_EEE_NEG 0x20000000 /* EEE capability nego */ #define E1000_EEER_RX_LPI_STATUS 0x40000000 /* Rx in LPI state */ #define E1000_EEER_TX_LPI_STATUS 0x80000000 /* Tx in LPI state */ +#define E1000_EEE_LP_ADV_ADDR_I350 0x040F /* EEE LP Advertisement */ +#define E1000_M88E1543_PAGE_ADDR 0x16 /* Page Offset Register */ +#define E1000_M88E1543_EEE_CTRL_1 0x0 +#define E1000_M88E1543_EEE_CTRL_1_MS 0x0001 /* EEE Master/Slave */ +#define E1000_EEE_ADV_DEV_I354 7 +#define E1000_EEE_ADV_ADDR_I354 60 +#define E1000_EEE_ADV_100_SUPPORTED (1 << 1) /* 100BaseTx EEE Supported */ +#define E1000_EEE_ADV_1000_SUPPORTED (1 << 2) /* 1000BaseT EEE Supported */ +#define E1000_PCS_STATUS_DEV_I354 3 +#define E1000_PCS_STATUS_ADDR_I354 1 +#define E1000_PCS_STATUS_RX_LPI_RCVD 0x0400 +#define E1000_PCS_STATUS_TX_LPI_RCVD 0x0800 #define E1000_EEE_SU_LPI_CLK_STP 0x00800000 /* EEE LPI Clock Stop */ +#define E1000_EEE_LP_ADV_DEV_I210 7 /* EEE LP Adv Device */ +#define E1000_EEE_LP_ADV_ADDR_I210 61 /* EEE LP Adv Register */ /* PCI Express Control */ #define E1000_GCR_RXD_NO_SNOOP 0x00000001 #define E1000_GCR_RXDSCW_NO_SNOOP 0x00000002 @@ -841,6 +875,8 @@ E1000_GCR_TXDSCW_NO_SNOOP | \ E1000_GCR_TXDSCR_NO_SNOOP) +#define E1000_MMDAC_FUNC_DATA 0x4000 /* Data, no post increment */ + /* mPHY address control and data registers */ #define E1000_MPHY_ADDR_CTL 0x0024 /* Address Control Reg */ #define E1000_MPHY_ADDR_CTL_OFFSET_MASK 0xFFFF0000 @@ -1169,6 +1205,8 @@ #define M88E1011_I_PHY_ID 0x01410C20 #define IGP01E1000_I_PHY_ID 0x02A80380 #define M88E1111_I_PHY_ID 0x01410CC0 +#define M88E1543_E_PHY_ID 0x01410EA0 +#define M88E1512_E_PHY_ID 0x01410DD0 #define M88E1112_E_PHY_ID 0x01410C90 #define I347AT4_E_PHY_ID 0x01410DC0 #define M88E1340M_E_PHY_ID 0x01410DF0 @@ -1416,4 +1454,6 @@ /* Lan ID bit field offset in status register */ #define E1000_STATUS_LAN_ID_OFFSET 2 #define E1000_VFTA_ENTRIES 128 +#define E1000_UNUSEDARG +#define ERROR_REPORT(fmt) do { } while (0) #endif /* _E1000_DEFINES_H_ */ diff --git a/sys/dev/e1000/e1000_hw.h b/sys/dev/e1000/e1000_hw.h index e8a8c174ca0..8901ccd11a8 100644 --- a/sys/dev/e1000/e1000_hw.h +++ b/sys/dev/e1000/e1000_hw.h @@ -165,7 +165,12 @@ struct e1000_hw; #define E1000_DEV_ID_I210_FIBER 0x1536 #define E1000_DEV_ID_I210_SERDES 0x1537 #define E1000_DEV_ID_I210_SGMII 0x1538 +#define E1000_DEV_ID_I210_COPPER_FLASHLESS 0x157B +#define E1000_DEV_ID_I210_SERDES_FLASHLESS 0x157C #define E1000_DEV_ID_I211_COPPER 0x1539 +#define E1000_DEV_ID_I354_BACKPLANE_1GBPS 0x1F40 +#define E1000_DEV_ID_I354_SGMII 0x1F41 +#define E1000_DEV_ID_I354_BACKPLANE_2_5GBPS 0x1F45 #define E1000_DEV_ID_DH89XXCC_SGMII 0x0438 #define E1000_DEV_ID_DH89XXCC_SERDES 0x043A #define E1000_DEV_ID_DH89XXCC_BACKPLANE 0x043C @@ -217,6 +222,7 @@ enum e1000_mac_type { e1000_82576, e1000_82580, e1000_i350, + e1000_i354, e1000_i210, e1000_i211, e1000_vfadapt, @@ -238,6 +244,7 @@ enum e1000_nvm_type { e1000_nvm_eeprom_spi, e1000_nvm_eeprom_microwire, e1000_nvm_flash_hw, + e1000_nvm_invm, e1000_nvm_flash_sw }; @@ -391,6 +398,10 @@ union e1000_rx_desc_extended { }; #define MAX_PS_BUFFERS 4 + +/* Number of packet split data buffers (not including the header buffer) */ +#define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) + /* Receive Descriptor - Packet Split */ union e1000_rx_desc_packet_split { struct { @@ -415,7 +426,8 @@ union e1000_rx_desc_packet_split { } middle; struct { __le16 header_status; - __le16 length[3]; /* length of buffers 1-3 */ + /* length of buffers 1-3 */ + __le16 length[PS_PAGE_BUFFERS]; } upper; __le64 reserved; } wb; /* writeback */ @@ -940,6 +952,8 @@ struct e1000_dev_spec_82575 { bool clear_semaphore_once; u32 mtu; struct sfp_e1000_flags eth_flags; + u8 media_port; + bool media_changed; }; struct e1000_dev_spec_vf { diff --git a/sys/dev/e1000/e1000_i210.c b/sys/dev/e1000/e1000_i210.c index 63302c0d376..019e4a9b080 100644 --- a/sys/dev/e1000/e1000_i210.c +++ b/sys/dev/e1000/e1000_i210.c @@ -42,8 +42,6 @@ static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); static s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw); static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data); -static s32 e1000_read_nvm_i211(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data); /** * e1000_acquire_nvm_i210 - Request for access to EEPROM @@ -180,9 +178,8 @@ static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw) } if (i == timeout) { - /* - * In rare circumstances, the driver may not have released the - * SW semaphore. Clear the semaphore once before giving up. + /* In rare circumstances, the SW semaphore may already be held + * unintentionally. Clear the semaphore once before giving up. */ if (hw->dev_spec._82575.clear_semaphore_once) { hw->dev_spec._82575.clear_semaphore_once = FALSE; @@ -368,87 +365,7 @@ out: return ret_val; } -/** - * e1000_read_nvm_i211 - Read NVM wrapper function for I211 - * @hw: pointer to the HW structure - * @address: the word address (aka eeprom offset) to read - * @data: pointer to the data read - * - * Wrapper function to return data formerly found in the NVM. - **/ -static s32 e1000_read_nvm_i211(struct e1000_hw *hw, u16 offset, - u16 words, u16 *data) -{ - s32 ret_val = E1000_SUCCESS; - - DEBUGFUNC("e1000_read_nvm_i211"); - - /* Only the MAC addr is required to be present in the iNVM */ - switch (offset) { - case NVM_MAC_ADDR: - ret_val = e1000_read_invm_i211(hw, (u8)offset, &data[0]); - ret_val |= e1000_read_invm_i211(hw, (u8)offset+1, &data[1]); - ret_val |= e1000_read_invm_i211(hw, (u8)offset+2, &data[2]); - if (ret_val != E1000_SUCCESS) - DEBUGOUT("MAC Addr not found in iNVM\n"); - break; - case NVM_INIT_CTRL_2: - ret_val = e1000_read_invm_i211(hw, (u8)offset, data); - if (ret_val != E1000_SUCCESS) { - *data = NVM_INIT_CTRL_2_DEFAULT_I211; - ret_val = E1000_SUCCESS; - } - break; - case NVM_INIT_CTRL_4: - ret_val = e1000_read_invm_i211(hw, (u8)offset, data); - if (ret_val != E1000_SUCCESS) { - *data = NVM_INIT_CTRL_4_DEFAULT_I211; - ret_val = E1000_SUCCESS; - } - break; - case NVM_LED_1_CFG: - ret_val = e1000_read_invm_i211(hw, (u8)offset, data); - if (ret_val != E1000_SUCCESS) { - *data = NVM_LED_1_CFG_DEFAULT_I211; - ret_val = E1000_SUCCESS; - } - break; - case NVM_LED_0_2_CFG: - ret_val = e1000_read_invm_i211(hw, (u8)offset, data); - if (ret_val != E1000_SUCCESS) { - *data = NVM_LED_0_2_CFG_DEFAULT_I211; - ret_val = E1000_SUCCESS; - } - break; - case NVM_ID_LED_SETTINGS: - ret_val = e1000_read_invm_i211(hw, (u8)offset, data); - if (ret_val != E1000_SUCCESS) { - *data = ID_LED_RESERVED_FFFF; - ret_val = E1000_SUCCESS; - } - break; - case NVM_SUB_DEV_ID: - *data = hw->subsystem_device_id; - break; - case NVM_SUB_VEN_ID: - *data = hw->subsystem_vendor_id; - break; - case NVM_DEV_ID: - *data = hw->device_id; - break; - case NVM_VEN_ID: - *data = hw->vendor_id; - break; - default: - DEBUGOUT1("NVM word 0x%02x is not mapped.\n", offset); - *data = NVM_RESERVED_WORD; - break; - } - return ret_val; -} - -/** - * e1000_read_invm_i211 - Reads OTP +/** e1000_read_invm_word_i210 - Reads OTP * @hw: pointer to the HW structure * @address: the word address (aka eeprom offset) to read * @data: pointer to the data read @@ -456,14 +373,14 @@ static s32 e1000_read_nvm_i211(struct e1000_hw *hw, u16 offset, * Reads 16-bit words from the OTP. Return error when the word is not * stored in OTP. **/ -s32 e1000_read_invm_i211(struct e1000_hw *hw, u8 address, u16 *data) +static s32 e1000_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data) { s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; u32 invm_dword; u16 i; u8 record_type, word_address; - DEBUGFUNC("e1000_read_invm_i211"); + DEBUGFUNC("e1000_read_invm_word_i210"); for (i = 0; i < E1000_INVM_SIZE; i++) { invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i)); @@ -491,6 +408,86 @@ s32 e1000_read_invm_i211(struct e1000_hw *hw, u8 address, u16 *data) return status; } +/** e1000_read_invm_i210 - Read invm wrapper function for I210/I211 + * @hw: pointer to the HW structure + * @address: the word address (aka eeprom offset) to read + * @data: pointer to the data read + * + * Wrapper function to return data formerly found in the NVM. + **/ +static s32 e1000_read_invm_i210(struct e1000_hw *hw, u16 offset, + u16 E1000_UNUSEDARG words, u16 *data) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_invm_i210"); + + /* Only the MAC addr is required to be present in the iNVM */ + switch (offset) { + case NVM_MAC_ADDR: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, &data[0]); + ret_val |= e1000_read_invm_word_i210(hw, (u8)offset+1, + &data[1]); + ret_val |= e1000_read_invm_word_i210(hw, (u8)offset+2, + &data[2]); + if (ret_val != E1000_SUCCESS) + DEBUGOUT("MAC Addr not found in iNVM\n"); + break; + case NVM_INIT_CTRL_2: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_INIT_CTRL_2_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_INIT_CTRL_4: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_INIT_CTRL_4_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_LED_1_CFG: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_LED_1_CFG_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_LED_0_2_CFG: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_LED_0_2_CFG_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_ID_LED_SETTINGS: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = ID_LED_RESERVED_FFFF; + ret_val = E1000_SUCCESS; + } + break; + case NVM_SUB_DEV_ID: + *data = hw->subsystem_device_id; + break; + case NVM_SUB_VEN_ID: + *data = hw->subsystem_vendor_id; + break; + case NVM_DEV_ID: + *data = hw->device_id; + break; + case NVM_VEN_ID: + *data = hw->vendor_id; + break; + default: + DEBUGOUT1("NVM word 0x%02x is not mapped.\n", offset); + *data = NVM_RESERVED_WORD; + break; + } + return ret_val; +} + /** * e1000_validate_nvm_checksum_i210 - Validate EEPROM checksum * @hw: pointer to the HW structure @@ -591,6 +588,26 @@ out: return ret_val; } +/** + * e1000_get_flash_presence_i210 - Check if flash device is detected. + * @hw: pointer to the HW structure + * + **/ +bool e1000_get_flash_presence_i210(struct e1000_hw *hw) +{ + u32 eec = 0; + bool ret_val = FALSE; + + DEBUGFUNC("e1000_get_flash_presence_i210"); + + eec = E1000_READ_REG(hw, E1000_EECD); + + if (eec & E1000_EECD_FLASH_DETECTED_I210) + ret_val = TRUE; + + return ret_val; +} + /** * e1000_update_flash_i210 - Commit EEPROM to the flash * @hw: pointer to the HW structure @@ -650,7 +667,7 @@ s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw) * e1000_init_nvm_params_i210 - Initialize i210 NVM function pointers * @hw: pointer to the HW structure * - * Initialize the i210 NVM parameters and function pointers. + * Initialize the i210/i211 NVM parameters and function pointers. **/ static s32 e1000_init_nvm_params_i210(struct e1000_hw *hw) { @@ -660,41 +677,25 @@ static s32 e1000_init_nvm_params_i210(struct e1000_hw *hw) DEBUGFUNC("e1000_init_nvm_params_i210"); ret_val = e1000_init_nvm_params_82575(hw); - nvm->ops.acquire = e1000_acquire_nvm_i210; nvm->ops.release = e1000_release_nvm_i210; - nvm->ops.read = e1000_read_nvm_srrd_i210; - nvm->ops.write = e1000_write_nvm_srwr_i210; nvm->ops.valid_led_default = e1000_valid_led_default_i210; - nvm->ops.validate = e1000_validate_nvm_checksum_i210; - nvm->ops.update = e1000_update_nvm_checksum_i210; - + if (e1000_get_flash_presence_i210(hw)) { + hw->nvm.type = e1000_nvm_flash_hw; + nvm->ops.read = e1000_read_nvm_srrd_i210; + nvm->ops.write = e1000_write_nvm_srwr_i210; + nvm->ops.validate = e1000_validate_nvm_checksum_i210; + nvm->ops.update = e1000_update_nvm_checksum_i210; + } else { + hw->nvm.type = e1000_nvm_invm; + nvm->ops.read = e1000_read_invm_i210; + nvm->ops.write = e1000_null_write_nvm; + nvm->ops.validate = e1000_null_ops_generic; + nvm->ops.update = e1000_null_ops_generic; + } return ret_val; } -/** - * e1000_init_nvm_params_i211 - Initialize i211 NVM function pointers - * @hw: pointer to the HW structure - * - * Initialize the NVM parameters and function pointers for i211. - **/ -static s32 e1000_init_nvm_params_i211(struct e1000_hw *hw) -{ - struct e1000_nvm_info *nvm = &hw->nvm; - - DEBUGFUNC("e1000_init_nvm_params_i211"); - - nvm->ops.acquire = e1000_acquire_nvm_i210; - nvm->ops.release = e1000_release_nvm_i210; - nvm->ops.read = e1000_read_nvm_i211; - nvm->ops.valid_led_default = e1000_valid_led_default_i210; - nvm->ops.write = e1000_null_write_nvm; - nvm->ops.validate = e1000_null_ops_generic; - nvm->ops.update = e1000_null_ops_generic; - - return E1000_SUCCESS; -} - /** * e1000_init_function_pointers_i210 - Init func ptrs. * @hw: pointer to the HW structure @@ -704,17 +705,8 @@ static s32 e1000_init_nvm_params_i211(struct e1000_hw *hw) void e1000_init_function_pointers_i210(struct e1000_hw *hw) { e1000_init_function_pointers_82575(hw); + hw->nvm.ops.init_params = e1000_init_nvm_params_i210; - switch (hw->mac.type) { - case e1000_i210: - hw->nvm.ops.init_params = e1000_init_nvm_params_i210; - break; - case e1000_i211: - hw->nvm.ops.init_params = e1000_init_nvm_params_i211; - break; - default: - break; - } return; } @@ -752,3 +744,74 @@ static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data) out: return ret_val; } + +/** + * __e1000_access_xmdio_reg - Read/write XMDIO register + * @hw: pointer to the HW structure + * @address: XMDIO address to program + * @dev_addr: device address to program + * @data: pointer to value to read/write from/to the XMDIO address + * @read: boolean flag to indicate read or write + **/ +static s32 __e1000_access_xmdio_reg(struct e1000_hw *hw, u16 address, + u8 dev_addr, u16 *data, bool read) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("__e1000_access_xmdio_reg"); + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, dev_addr); + if (ret_val) + return ret_val; + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, address); + if (ret_val) + return ret_val; + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, E1000_MMDAC_FUNC_DATA | + dev_addr); + if (ret_val) + return ret_val; + + if (read) + ret_val = hw->phy.ops.read_reg(hw, E1000_MMDAAD, data); + else + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, *data); + if (ret_val) + return ret_val; + + /* Recalibrate the device back to 0 */ + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, 0); + if (ret_val) + return ret_val; + + return ret_val; +} + +/** + * e1000_read_xmdio_reg - Read XMDIO register + * @hw: pointer to the HW structure + * @addr: XMDIO address to program + * @dev_addr: device address to program + * @data: value to be read from the EMI address + **/ +s32 e1000_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 *data) +{ + DEBUGFUNC("e1000_read_xmdio_reg"); + + return __e1000_access_xmdio_reg(hw, addr, dev_addr, data, TRUE); +} + +/** + * e1000_write_xmdio_reg - Write XMDIO register + * @hw: pointer to the HW structure + * @addr: XMDIO address to program + * @dev_addr: device address to program + * @data: value to be written to the XMDIO address + **/ +s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data) +{ + DEBUGFUNC("e1000_read_xmdio_reg"); + + return __e1000_access_xmdio_reg(hw, addr, dev_addr, &data, FALSE); +} diff --git a/sys/dev/e1000/e1000_i210.h b/sys/dev/e1000/e1000_i210.h index d7711fe093f..d8de1bd730f 100644 --- a/sys/dev/e1000/e1000_i210.h +++ b/sys/dev/e1000/e1000_i210.h @@ -35,6 +35,7 @@ #ifndef _E1000_I210_H_ #define _E1000_I210_H_ +bool e1000_get_flash_presence_i210(struct e1000_hw *hw); s32 e1000_update_flash_i210(struct e1000_hw *hw); s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw); s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw); @@ -42,9 +43,12 @@ s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); -s32 e1000_read_invm_i211(struct e1000_hw *hw, u8 address, u16 *data); s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask); void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask); +s32 e1000_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, + u16 *data); +s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, + u16 data); #define E1000_STM_OPCODE 0xDB00 #define E1000_EEPROM_FLASH_SIZE_WORD 0x11 @@ -82,7 +86,7 @@ enum E1000_INVM_STRUCTURE_TYPE { (ID_LED_OFF1_OFF2)) #define ID_LED_DEFAULT_I210_SERDES ((ID_LED_DEF1_DEF2 << 8) | \ (ID_LED_DEF1_DEF2 << 4) | \ - (ID_LED_DEF1_DEF2)) + (ID_LED_OFF1_ON2)) /* NVM offset defaults for I211 devices */ #define NVM_INIT_CTRL_2_DEFAULT_I211 0X7243 diff --git a/sys/dev/e1000/e1000_ich8lan.c b/sys/dev/e1000/e1000_ich8lan.c index bd943555275..82ffceb3e1e 100644 --- a/sys/dev/e1000/e1000_ich8lan.c +++ b/sys/dev/e1000/e1000_ich8lan.c @@ -59,6 +59,10 @@ * 82578DC Gigabit Network Connection * 82579LM Gigabit Network Connection * 82579V Gigabit Network Connection + * Ethernet Connection I217-LM + * Ethernet Connection I217-V + * Ethernet Connection I218-V + * Ethernet Connection I218-LM */ #include "e1000_api.h" @@ -71,6 +75,7 @@ static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw); static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw); static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index); static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index); +static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw); static void e1000_update_mc_addr_list_pch2lan(struct e1000_hw *hw, u8 *mc_addr_list, u32 mc_addr_count); @@ -181,8 +186,9 @@ static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw) { u16 phy_reg = 0; u32 phy_id = 0; - s32 ret_val; + s32 ret_val = 0; u16 retry_count; + u32 mac_reg = 0; for (retry_count = 0; retry_count < 2; retry_count++) { ret_val = hw->phy.ops.read_reg_locked(hw, PHY_ID1, &phy_reg); @@ -201,23 +207,84 @@ static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw) if (hw->phy.id) { if (hw->phy.id == phy_id) - return TRUE; + goto out; } else if (phy_id) { hw->phy.id = phy_id; hw->phy.revision = (u32)(phy_reg & ~PHY_REVISION_MASK); - return TRUE; + goto out; } /* In case the PHY needs to be in mdio slow mode, * set slow mode and try to get the PHY id again. */ - hw->phy.ops.release(hw); - ret_val = e1000_set_mdio_slow_mode_hv(hw); - if (!ret_val) - ret_val = e1000_get_phy_id(hw); - hw->phy.ops.acquire(hw); + if (hw->mac.type < e1000_pch_lpt) { + hw->phy.ops.release(hw); + ret_val = e1000_set_mdio_slow_mode_hv(hw); + if (!ret_val) + ret_val = e1000_get_phy_id(hw); + hw->phy.ops.acquire(hw); + } - return !ret_val; + if (ret_val) + return FALSE; +out: + if (hw->mac.type == e1000_pch_lpt) { + /* Unforce SMBus mode in PHY */ + hw->phy.ops.read_reg_locked(hw, CV_SMB_CTRL, &phy_reg); + phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS; + hw->phy.ops.write_reg_locked(hw, CV_SMB_CTRL, phy_reg); + + /* Unforce SMBus mode in MAC */ + mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT); + mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg); + } + + return TRUE; +} + +/** + * e1000_toggle_lanphypc_pch_lpt - toggle the LANPHYPC pin value + * @hw: pointer to the HW structure + * + * Toggling the LANPHYPC pin value fully power-cycles the PHY and is + * used to reset the PHY to a quiescent state when necessary. + **/ +void e1000_toggle_lanphypc_pch_lpt(struct e1000_hw *hw) +{ + u32 mac_reg; + + DEBUGFUNC("e1000_toggle_lanphypc_pch_lpt"); + + /* Set Phy Config Counter to 50msec */ + mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM3); + mac_reg &= ~E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK; + mac_reg |= E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC; + E1000_WRITE_REG(hw, E1000_FEXTNVM3, mac_reg); + + /* Toggle LANPHYPC Value bit */ + mac_reg = E1000_READ_REG(hw, E1000_CTRL); + mac_reg |= E1000_CTRL_LANPHYPC_OVERRIDE; + mac_reg &= ~E1000_CTRL_LANPHYPC_VALUE; + E1000_WRITE_REG(hw, E1000_CTRL, mac_reg); + E1000_WRITE_FLUSH(hw); + usec_delay(10); + mac_reg &= ~E1000_CTRL_LANPHYPC_OVERRIDE; + E1000_WRITE_REG(hw, E1000_CTRL, mac_reg); + E1000_WRITE_FLUSH(hw); + + if (hw->mac.type < e1000_pch_lpt) { + msec_delay(50); + } else { + u16 count = 20; + + do { + msec_delay(5); + } while (!(E1000_READ_REG(hw, E1000_CTRL_EXT) & + E1000_CTRL_EXT_LPCD) && count--); + + msec_delay(30); + } } /** @@ -231,7 +298,6 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) { u32 mac_reg, fwsm = E1000_READ_REG(hw, E1000_FWSM); s32 ret_val; - u16 phy_reg; DEBUGFUNC("e1000_init_phy_workarounds_pchlan"); @@ -262,24 +328,16 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS; E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg); + /* Wait 50 milliseconds for MAC to finish any retries + * that it might be trying to perform from previous + * attempts to acknowledge any phy read requests. + */ + msec_delay(50); + /* fall-through */ case e1000_pch2lan: - if (e1000_phy_is_accessible_pchlan(hw)) { - if (hw->mac.type == e1000_pch_lpt) { - /* Unforce SMBus mode in PHY */ - hw->phy.ops.read_reg_locked(hw, CV_SMB_CTRL, - &phy_reg); - phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS; - hw->phy.ops.write_reg_locked(hw, CV_SMB_CTRL, - phy_reg); - - /* Unforce SMBus mode in MAC */ - mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT); - mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS; - E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg); - } + if (e1000_phy_is_accessible_pchlan(hw)) break; - } /* fall-through */ case e1000_pchlan: @@ -289,44 +347,27 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) if (hw->phy.ops.check_reset_block(hw)) { DEBUGOUT("Required LANPHYPC toggle blocked by ME\n"); + ret_val = -E1000_ERR_PHY; break; } - DEBUGOUT("Toggling LANPHYPC\n"); + /* Toggle LANPHYPC Value bit */ + e1000_toggle_lanphypc_pch_lpt(hw); + if (hw->mac.type >= e1000_pch_lpt) { + if (e1000_phy_is_accessible_pchlan(hw)) + break; - /* Set Phy Config Counter to 50msec */ - mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM3); - mac_reg &= ~E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK; - mac_reg |= E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC; - E1000_WRITE_REG(hw, E1000_FEXTNVM3, mac_reg); - - if (hw->mac.type == e1000_pch_lpt) { /* Toggling LANPHYPC brings the PHY out of SMBus mode - * So ensure that the MAC is also out of SMBus mode + * so ensure that the MAC is also out of SMBus mode */ mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT); mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS; E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg); - } - /* Toggle LANPHYPC Value bit */ - mac_reg = E1000_READ_REG(hw, E1000_CTRL); - mac_reg |= E1000_CTRL_LANPHYPC_OVERRIDE; - mac_reg &= ~E1000_CTRL_LANPHYPC_VALUE; - E1000_WRITE_REG(hw, E1000_CTRL, mac_reg); - E1000_WRITE_FLUSH(hw); - usec_delay(10); - mac_reg &= ~E1000_CTRL_LANPHYPC_OVERRIDE; - E1000_WRITE_REG(hw, E1000_CTRL, mac_reg); - E1000_WRITE_FLUSH(hw); - if (hw->mac.type < e1000_pch_lpt) { - msec_delay(50); - } else { - u16 count = 20; - do { - msec_delay(5); - } while (!(E1000_READ_REG(hw, E1000_CTRL_EXT) & - E1000_CTRL_EXT_LPCD) && count--); + if (e1000_phy_is_accessible_pchlan(hw)) + break; + + ret_val = -E1000_ERR_PHY; } break; default: @@ -334,13 +375,33 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) } hw->phy.ops.release(hw); + if (!ret_val) { - /* Reset the PHY before any access to it. Doing so, ensures - * that the PHY is in a known good state before we read/write - * PHY registers. The generic reset is sufficient here, - * because we haven't determined the PHY type yet. - */ - ret_val = e1000_phy_hw_reset_generic(hw); + /* Check to see if able to reset PHY. Print error if not */ + if (hw->phy.ops.check_reset_block(hw)) { + ERROR_REPORT("Reset blocked by ME\n"); + goto out; + } + + /* Reset the PHY before any access to it. Doing so, ensures + * that the PHY is in a known good state before we read/write + * PHY registers. The generic reset is sufficient here, + * because we haven't determined the PHY type yet. + */ + ret_val = e1000_phy_hw_reset_generic(hw); + if (ret_val) + goto out; + + /* On a successful reset, possibly need to wait for the PHY + * to quiesce to an accessible state before returning control + * to the calling function. If the PHY does not quiesce, then + * return E1000E_BLK_PHY_RESET, as this is the condition that + * the PHY is in. + */ + ret_val = hw->phy.ops.check_reset_block(hw); + if (ret_val) + ERROR_REPORT("ME blocked access to PHY after reset\n"); + } out: /* Ungate automatic PHY configuration on non-managed 82579 */ @@ -572,8 +633,8 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) /* find total size of the NVM, then cut in half since the total * size represents two separate NVM banks. */ - nvm->flash_bank_size = (sector_end_addr - sector_base_addr) - << FLASH_SECTOR_ADDR_SHIFT; + nvm->flash_bank_size = ((sector_end_addr - sector_base_addr) + << FLASH_SECTOR_ADDR_SHIFT); nvm->flash_bank_size /= 2; /* Adjust to word count */ nvm->flash_bank_size /= sizeof(u16); @@ -766,7 +827,7 @@ s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data) * * Assumes the SW/FW/HW Semaphore is already acquired. **/ -static s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data) +s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data) { DEBUGFUNC("e1000_read_emi_reg_locked"); @@ -780,18 +841,35 @@ static s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data) * Enable/disable EEE based on setting in dev_spec structure, the duplex of * the link and the EEE capabilities of the link partner. The LPI Control * register bits will remain set only if/when link is up. + * + * EEE LPI must not be asserted earlier than one second after link is up. + * On 82579, EEE LPI should not be enabled until such time otherwise there + * can be link issues with some switches. Other devices can have EEE LPI + * enabled immediately upon link up since they have a timer in hardware which + * prevents LPI from being asserted too early. **/ -static s32 e1000_set_eee_pchlan(struct e1000_hw *hw) +s32 e1000_set_eee_pchlan(struct e1000_hw *hw) { struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan; s32 ret_val; - u16 lpi_ctrl; + u16 lpa, pcs_status, adv, adv_addr, lpi_ctrl, data; DEBUGFUNC("e1000_set_eee_pchlan"); - if ((hw->phy.type != e1000_phy_82579) && - (hw->phy.type != e1000_phy_i217)) + switch (hw->phy.type) { + case e1000_phy_82579: + lpa = I82579_EEE_LP_ABILITY; + pcs_status = I82579_EEE_PCS_STATUS; + adv_addr = I82579_EEE_ADVERTISEMENT; + break; + case e1000_phy_i217: + lpa = I217_EEE_LP_ABILITY; + pcs_status = I217_EEE_PCS_STATUS; + adv_addr = I217_EEE_ADVERTISEMENT; + break; + default: return E1000_SUCCESS; + } ret_val = hw->phy.ops.acquire(hw); if (ret_val) @@ -806,34 +884,24 @@ static s32 e1000_set_eee_pchlan(struct e1000_hw *hw) /* Enable EEE if not disabled by user */ if (!dev_spec->eee_disable) { - u16 lpa, pcs_status, data; - /* Save off link partner's EEE ability */ - switch (hw->phy.type) { - case e1000_phy_82579: - lpa = I82579_EEE_LP_ABILITY; - pcs_status = I82579_EEE_PCS_STATUS; - break; - case e1000_phy_i217: - lpa = I217_EEE_LP_ABILITY; - pcs_status = I217_EEE_PCS_STATUS; - break; - default: - ret_val = -E1000_ERR_PHY; - goto release; - } ret_val = e1000_read_emi_reg_locked(hw, lpa, &dev_spec->eee_lp_ability); if (ret_val) goto release; + /* Read EEE advertisement */ + ret_val = e1000_read_emi_reg_locked(hw, adv_addr, &adv); + if (ret_val) + goto release; + /* Enable EEE only for speeds in which the link partner is - * EEE capable. + * EEE capable and for which we advertise EEE. */ - if (dev_spec->eee_lp_ability & I82579_EEE_1000_SUPPORTED) + if (adv & dev_spec->eee_lp_ability & I82579_EEE_1000_SUPPORTED) lpi_ctrl |= I82579_LPI_CTRL_1000_ENABLE; - if (dev_spec->eee_lp_ability & I82579_EEE_100_SUPPORTED) { + if (adv & dev_spec->eee_lp_ability & I82579_EEE_100_SUPPORTED) { hw->phy.ops.read_reg_locked(hw, PHY_LP_ABILITY, &data); if (data & NWAY_LPAR_100TX_FD_CAPS) lpi_ctrl |= I82579_LPI_CTRL_100_ENABLE; @@ -845,13 +913,13 @@ static s32 e1000_set_eee_pchlan(struct e1000_hw *hw) dev_spec->eee_lp_ability &= ~I82579_EEE_100_SUPPORTED; } - - /* R/Clr IEEE MMD 3.1 bits 11:10 - Tx/Rx LPI Received */ - ret_val = e1000_read_emi_reg_locked(hw, pcs_status, &data); - if (ret_val) - goto release; } + /* R/Clr IEEE MMD 3.1 bits 11:10 - Tx/Rx LPI Received */ + ret_val = e1000_read_emi_reg_locked(hw, pcs_status, &data); + if (ret_val) + goto release; + ret_val = hw->phy.ops.write_reg_locked(hw, I82579_LPI_CTRL, lpi_ctrl); release: hw->phy.ops.release(hw); @@ -867,30 +935,31 @@ release: * When K1 is enabled for 1Gbps, the MAC can miss 2 DMA completion indications * preventing further DMA write requests. Workaround the issue by disabling * the de-assertion of the clock request when in 1Gpbs mode. + * Also, set appropriate Tx re-transmission timeouts for 10 and 100Half link + * speeds in order to avoid Tx hangs. **/ static s32 e1000_k1_workaround_lpt_lp(struct e1000_hw *hw, bool link) { u32 fextnvm6 = E1000_READ_REG(hw, E1000_FEXTNVM6); + u32 status = E1000_READ_REG(hw, E1000_STATUS); s32 ret_val = E1000_SUCCESS; + u16 reg; - if (link && (E1000_READ_REG(hw, E1000_STATUS) & - E1000_STATUS_SPEED_1000)) { - u16 kmrn_reg; - + if (link && (status & E1000_STATUS_SPEED_1000)) { ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; ret_val = e1000_read_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_K1_CONFIG, - &kmrn_reg); + ®); if (ret_val) goto release; ret_val = e1000_write_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_K1_CONFIG, - kmrn_reg & + reg & ~E1000_KMRNCTRLSTA_K1_ENABLE); if (ret_val) goto release; @@ -903,13 +972,45 @@ static s32 e1000_k1_workaround_lpt_lp(struct e1000_hw *hw, bool link) ret_val = e1000_write_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_K1_CONFIG, - kmrn_reg); + reg); release: hw->phy.ops.release(hw); } else { /* clear FEXTNVM6 bit 8 on link down or 10/100 */ - E1000_WRITE_REG(hw, E1000_FEXTNVM6, - fextnvm6 & ~E1000_FEXTNVM6_REQ_PLL_CLK); + fextnvm6 &= ~E1000_FEXTNVM6_REQ_PLL_CLK; + + if (!link || ((status & E1000_STATUS_SPEED_100) && + (status & E1000_STATUS_FD))) + goto update_fextnvm6; + + ret_val = hw->phy.ops.read_reg(hw, I217_INBAND_CTRL, ®); + if (ret_val) + return ret_val; + + /* Clear link status transmit timeout */ + reg &= ~I217_INBAND_CTRL_LINK_STAT_TX_TIMEOUT_MASK; + + if (status & E1000_STATUS_SPEED_100) { + /* Set inband Tx timeout to 5x10us for 100Half */ + reg |= 5 << I217_INBAND_CTRL_LINK_STAT_TX_TIMEOUT_SHIFT; + + /* Do not extend the K1 entry latency for 100Half */ + fextnvm6 &= ~E1000_FEXTNVM6_ENABLE_K1_ENTRY_CONDITION; + } else { + /* Set inband Tx timeout to 50x10us for 10Full/Half */ + reg |= 50 << + I217_INBAND_CTRL_LINK_STAT_TX_TIMEOUT_SHIFT; + + /* Extend the K1 entry latency for 10 Mbps */ + fextnvm6 |= E1000_FEXTNVM6_ENABLE_K1_ENTRY_CONDITION; + } + + ret_val = hw->phy.ops.write_reg(hw, I217_INBAND_CTRL, reg); + if (ret_val) + return ret_val; + +update_fextnvm6: + E1000_WRITE_REG(hw, E1000_FEXTNVM6, fextnvm6); } return ret_val; @@ -1018,7 +1119,6 @@ static s32 e1000_platform_pm_pch_lpt(struct e1000_hw *hw, bool link) lat_ns /= 1000000000; obff_hwm = (s32)(rxa - lat_ns); } - if ((obff_hwm < 0) || (obff_hwm > E1000_SVT_OFF_HWM_MASK)) { DEBUGOUT1("Invalid high water mark %d\n", obff_hwm); return -E1000_ERR_CONFIG; @@ -1103,13 +1203,13 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) if (!mac->get_link_status) return E1000_SUCCESS; - /* First we want to see if the MII Status Register reports - * link. If so, then we want to get the current speed/duplex - * of the PHY. - */ - ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); - if (ret_val) - return ret_val; + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + */ + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; if (hw->mac.type == e1000_pchlan) { ret_val = e1000_k1_gig_workaround_hv(hw, link); @@ -1153,8 +1253,9 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) } if (hw->mac.type == e1000_pch_lpt) { - /* Set platform power management values for Latency Tolerance - * Reporting (LTR) and Optimized Buffer Flush/Fill (OBFF). + /* Set platform power management values for + * Latency Tolerance Reporting (LTR) + * Optimized Buffer Flush/Fill (OBFF) */ ret_val = e1000_platform_pm_pch_lpt(hw, link); if (ret_val) @@ -1206,9 +1307,11 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) e1000_check_downshift_generic(hw); /* Enable/Disable EEE after link up */ - ret_val = e1000_set_eee_pchlan(hw); - if (ret_val) - return ret_val; + if (hw->phy.type > e1000_phy_82579) { + ret_val = e1000_set_eee_pchlan(hw); + if (ret_val) + return ret_val; + } /* If we are forcing speed/duplex, then we simply return since * we have already determined whether we have link or not. @@ -1396,9 +1499,9 @@ static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw) fwsm = E1000_READ_REG(hw, E1000_FWSM); - return (fwsm & E1000_ICH_FWSM_FW_VALID) && - ((fwsm & E1000_FWSM_MODE_MASK) == - (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)); + return ((fwsm & E1000_ICH_FWSM_FW_VALID) && + ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT))); } /** @@ -1459,7 +1562,10 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index) return; } - if (index < hw->mac.rar_entry_count) { + /* RAR[1-6] are owned by manageability. Skip those and program the + * next address into the SHRA register array. + */ + if (index < (u32) (hw->mac.rar_entry_count - 6)) { s32 ret_val; ret_val = e1000_acquire_swflag_ich8lan(hw); @@ -1619,13 +1725,21 @@ release: static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw) { u32 fwsm; + bool blocked = FALSE; + int i = 0; DEBUGFUNC("e1000_check_reset_block_ich8lan"); - fwsm = E1000_READ_REG(hw, E1000_FWSM); - - return (fwsm & E1000_ICH_FWSM_RSPCIPHY) ? E1000_SUCCESS - : E1000_BLK_PHY_RESET; + do { + fwsm = E1000_READ_REG(hw, E1000_FWSM); + if (!(fwsm & E1000_ICH_FWSM_RSPCIPHY)) { + blocked = TRUE; + msec_delay(10); + continue; + } + blocked = FALSE; + } while (blocked && (i++ < 10)); + return blocked ? E1000_BLK_PHY_RESET : E1000_SUCCESS; } /** @@ -1825,9 +1939,9 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link) if (ret_val) goto release; - status_reg &= BM_CS_STATUS_LINK_UP | - BM_CS_STATUS_RESOLVED | - BM_CS_STATUS_SPEED_MASK; + status_reg &= (BM_CS_STATUS_LINK_UP | + BM_CS_STATUS_RESOLVED | + BM_CS_STATUS_SPEED_MASK); if (status_reg == (BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED | @@ -1841,9 +1955,9 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link) if (ret_val) goto release; - status_reg &= HV_M_STATUS_LINK_UP | - HV_M_STATUS_AUTONEG_COMPLETE | - HV_M_STATUS_SPEED_MASK; + status_reg &= (HV_M_STATUS_LINK_UP | + HV_M_STATUS_AUTONEG_COMPLETE | + HV_M_STATUS_SPEED_MASK); if (status_reg == (HV_M_STATUS_LINK_UP | HV_M_STATUS_AUTONEG_COMPLETE | @@ -2125,8 +2239,8 @@ void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw) if (ret_val) goto release; - /* Copy both RAL/H (rar_entry_count) and SHRAL/H (+4) to PHY */ - for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) { + /* Copy both RAL/H (rar_entry_count) and SHRAL/H to PHY */ + for (i = 0; i < (hw->mac.rar_entry_count); i++) { mac_reg = E1000_READ_REG(hw, E1000_RAL(i)); hw->phy.ops.write_reg_page(hw, BM_RAR_L(i), (u16)(mac_reg & 0xFFFF)); @@ -2191,10 +2305,10 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) return ret_val; if (enable) { - /* Write Rx addresses (rar_entry_count for RAL/H, +4 for + /* Write Rx addresses (rar_entry_count for RAL/H, and * SHRAL/H) and initial CRC values to the MAC */ - for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) { + for (i = 0; i < hw->mac.rar_entry_count; i++) { u8 mac_addr[ETH_ADDR_LEN] = {0}; u32 addr_high, addr_low; @@ -3112,11 +3226,11 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, DEBUGFUNC("e1000_read_flash_data_ich8lan"); - if (size < 1 || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK) + if (size < 1 || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK) return -E1000_ERR_NVM; - flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & offset) + - hw->nvm.flash_base_addr; + flash_linear_addr = ((ICH_FLASH_LINEAR_ADDR_MASK & offset) + + hw->nvm.flash_base_addr); do { usec_delay(1); @@ -3133,8 +3247,9 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_addr); - ret_val = e1000_flash_cycle_ich8lan(hw, - ICH_FLASH_READ_COMMAND_TIMEOUT); + ret_val = + e1000_flash_cycle_ich8lan(hw, + ICH_FLASH_READ_COMMAND_TIMEOUT); /* Check if FCERR is set to 1, if set to 1, clear it * and try the whole sequence a few more times, else @@ -3437,8 +3552,8 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, offset > ICH_FLASH_LINEAR_ADDR_MASK) return -E1000_ERR_NVM; - flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & offset) + - hw->nvm.flash_base_addr; + flash_linear_addr = ((ICH_FLASH_LINEAR_ADDR_MASK & offset) + + hw->nvm.flash_base_addr); do { usec_delay(1); @@ -3465,8 +3580,9 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, /* check if FCERR is set to 1 , if set to 1, clear it * and try the whole sequence a few more times else done */ - ret_val = e1000_flash_cycle_ich8lan(hw, - ICH_FLASH_WRITE_COMMAND_TIMEOUT); + ret_val = + e1000_flash_cycle_ich8lan(hw, + ICH_FLASH_WRITE_COMMAND_TIMEOUT); if (ret_val == E1000_SUCCESS) break; @@ -3602,8 +3718,10 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) flash_linear_addr = hw->nvm.flash_base_addr; flash_linear_addr += (bank) ? flash_bank_size : 0; - for (j = 0; j < iteration ; j++) { + for (j = 0; j < iteration; j++) { do { + u32 timeout = ICH_FLASH_ERASE_COMMAND_TIMEOUT; + /* Steps */ ret_val = e1000_flash_cycle_init_ich8lan(hw); if (ret_val) @@ -3626,8 +3744,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_addr); - ret_val = e1000_flash_cycle_ich8lan(hw, - ICH_FLASH_ERASE_COMMAND_TIMEOUT); + ret_val = e1000_flash_cycle_ich8lan(hw, timeout); if (ret_val == E1000_SUCCESS) break; @@ -3947,16 +4064,16 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw) /* Set the transmit descriptor write-back policy for both queues */ txdctl = E1000_READ_REG(hw, E1000_TXDCTL(0)); - txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) | - E1000_TXDCTL_FULL_TX_DESC_WB; - txdctl = (txdctl & ~E1000_TXDCTL_PTHRESH) | - E1000_TXDCTL_MAX_TX_DESC_PREFETCH; + txdctl = ((txdctl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB); + txdctl = ((txdctl & ~E1000_TXDCTL_PTHRESH) | + E1000_TXDCTL_MAX_TX_DESC_PREFETCH); E1000_WRITE_REG(hw, E1000_TXDCTL(0), txdctl); txdctl = E1000_READ_REG(hw, E1000_TXDCTL(1)); - txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) | - E1000_TXDCTL_FULL_TX_DESC_WB; - txdctl = (txdctl & ~E1000_TXDCTL_PTHRESH) | - E1000_TXDCTL_MAX_TX_DESC_PREFETCH; + txdctl = ((txdctl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB); + txdctl = ((txdctl & ~E1000_TXDCTL_PTHRESH) | + E1000_TXDCTL_MAX_TX_DESC_PREFETCH); E1000_WRITE_REG(hw, E1000_TXDCTL(1), txdctl); /* ICH8 has opposite polarity of no_snoop bits. @@ -4041,6 +4158,7 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw) */ reg = E1000_READ_REG(hw, E1000_RFCTL); reg |= (E1000_RFCTL_NFSW_DIS | E1000_RFCTL_NFSR_DIS); + /* Disable IPv6 extension header parsing because some malformed * IPv6 headers can hang the Rx. */ @@ -4502,14 +4620,25 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw) /* Disable LPLU if both link partners support 100BaseT * EEE and 100Full is advertised on both ends of the - * link. + * link, and enable Auto Enable LPI since there will + * be no driver to enable LPI while in Sx. */ if ((eee_advert & I82579_EEE_100_SUPPORTED) && (dev_spec->eee_lp_ability & I82579_EEE_100_SUPPORTED) && - (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)) + (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)) { phy_ctrl &= ~(E1000_PHY_CTRL_D0A_LPLU | E1000_PHY_CTRL_NOND0A_LPLU); + + /* Set Auto Enable LPI after link up */ + hw->phy.ops.read_reg_locked(hw, + I217_LPI_GPIO_CTRL, + &phy_reg); + phy_reg |= I217_LPI_GPIO_CTRL_AUTO_EN_LPI; + hw->phy.ops.write_reg_locked(hw, + I217_LPI_GPIO_CTRL, + phy_reg); + } } /* For i217 Intel Rapid Start Technology support, @@ -4613,6 +4742,11 @@ void e1000_resume_workarounds_pchlan(struct e1000_hw *hw) return; } + /* Clear Auto Enable LPI after link up */ + hw->phy.ops.read_reg_locked(hw, I217_LPI_GPIO_CTRL, &phy_reg); + phy_reg &= ~I217_LPI_GPIO_CTRL_AUTO_EN_LPI; + hw->phy.ops.write_reg_locked(hw, I217_LPI_GPIO_CTRL, phy_reg); + if (!(E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID)) { /* Restore clear on SMB if no manageability engine diff --git a/sys/dev/e1000/e1000_ich8lan.h b/sys/dev/e1000/e1000_ich8lan.h index bf928981709..2dbf8127664 100644 --- a/sys/dev/e1000/e1000_ich8lan.h +++ b/sys/dev/e1000/e1000_ich8lan.h @@ -60,10 +60,10 @@ #define ICH_FLASH_SEG_SIZE_8K 8192 #define ICH_FLASH_SEG_SIZE_64K 65536 -#define E1000_ICH_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI Reset */ +#define E1000_ICH_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI Reset */ /* FW established a valid mode */ -#define E1000_ICH_FWSM_FW_VALID 0x00008000 -#define E1000_ICH_FWSM_PCIM2PCI 0x01000000 /* ME PCIm-to-PCI active */ +#define E1000_ICH_FWSM_FW_VALID 0x00008000 +#define E1000_ICH_FWSM_PCIM2PCI 0x01000000 /* ME PCIm-to-PCI active */ #define E1000_ICH_FWSM_PCIM2PCI_COUNT 2000 #define E1000_ICH_MNG_IAMT_MODE 0x2 @@ -88,7 +88,7 @@ #define E1000_ICH8_LAN_INIT_TIMEOUT 1500 #define E1000_FEXTNVM_SW_CONFIG 1 -#define E1000_FEXTNVM_SW_CONFIG_ICH8M (1 << 27) /* Bit redefined for ICH8M */ +#define E1000_FEXTNVM_SW_CONFIG_ICH8M (1 << 27) /* different on ICH8M */ #define E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK 0x0C000000 #define E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC 0x08000000 @@ -98,18 +98,19 @@ #define E1000_FEXTNVM4_BEACON_DURATION_16USEC 0x3 #define E1000_FEXTNVM6_REQ_PLL_CLK 0x00000100 +#define E1000_FEXTNVM6_ENABLE_K1_ENTRY_CONDITION 0x00000200 #define PCIE_ICH8_SNOOP_ALL PCIE_NO_SNOOP_ALL #define E1000_ICH_RAR_ENTRIES 7 -#define E1000_PCH2_RAR_ENTRIES 5 /* RAR[0], SHRA[0-3] */ +#define E1000_PCH2_RAR_ENTRIES 11 /* RAR[0-6], SHRA[0-3] */ #define E1000_PCH_LPT_RAR_ENTRIES 12 /* RAR[0], SHRA[0-10] */ #define PHY_PAGE_SHIFT 5 #define PHY_REG(page, reg) (((page) << PHY_PAGE_SHIFT) | \ ((reg) & MAX_PHY_REG_ADDRESS)) -#define IGP3_KMRN_DIAG PHY_REG(770, 19) /* KMRN Diagnostic */ -#define IGP3_VR_CTRL PHY_REG(776, 18) /* Voltage Regulator Control */ +#define IGP3_KMRN_DIAG PHY_REG(770, 19) /* KMRN Diagnostic */ +#define IGP3_VR_CTRL PHY_REG(776, 18) /* Voltage Regulator Control */ #define IGP3_KMRN_DIAG_PCS_LOCK_LOSS 0x0002 #define IGP3_VR_CTRL_DEV_POWERDOWN_MODE_MASK 0x0300 @@ -140,19 +141,20 @@ #define HV_MUX_DATA_CTRL_GEN_TO_MAC 0x0400 #define HV_MUX_DATA_CTRL_FORCE_SPEED 0x0004 #define HV_STATS_PAGE 778 -#define HV_SCC_UPPER PHY_REG(HV_STATS_PAGE, 16) /* Single Collision Count */ +/* Half-duplex collision counts */ +#define HV_SCC_UPPER PHY_REG(HV_STATS_PAGE, 16) /* Single Collision */ #define HV_SCC_LOWER PHY_REG(HV_STATS_PAGE, 17) -#define HV_ECOL_UPPER PHY_REG(HV_STATS_PAGE, 18) /* Excessive Coll. Count */ +#define HV_ECOL_UPPER PHY_REG(HV_STATS_PAGE, 18) /* Excessive Coll. */ #define HV_ECOL_LOWER PHY_REG(HV_STATS_PAGE, 19) -#define HV_MCC_UPPER PHY_REG(HV_STATS_PAGE, 20) /* Multiple Coll. Count */ +#define HV_MCC_UPPER PHY_REG(HV_STATS_PAGE, 20) /* Multiple Collision */ #define HV_MCC_LOWER PHY_REG(HV_STATS_PAGE, 21) -#define HV_LATECOL_UPPER PHY_REG(HV_STATS_PAGE, 23) /* Late Collision Count */ +#define HV_LATECOL_UPPER PHY_REG(HV_STATS_PAGE, 23) /* Late Collision */ #define HV_LATECOL_LOWER PHY_REG(HV_STATS_PAGE, 24) -#define HV_COLC_UPPER PHY_REG(HV_STATS_PAGE, 25) /* Collision Count */ +#define HV_COLC_UPPER PHY_REG(HV_STATS_PAGE, 25) /* Collision */ #define HV_COLC_LOWER PHY_REG(HV_STATS_PAGE, 26) #define HV_DC_UPPER PHY_REG(HV_STATS_PAGE, 27) /* Defer Count */ #define HV_DC_LOWER PHY_REG(HV_STATS_PAGE, 28) -#define HV_TNCRS_UPPER PHY_REG(HV_STATS_PAGE, 29) /* Transmit with no CRS */ +#define HV_TNCRS_UPPER PHY_REG(HV_STATS_PAGE, 29) /* Tx with no CRS */ #define HV_TNCRS_LOWER PHY_REG(HV_STATS_PAGE, 30) #define E1000_FCRTV_PCH 0x05F40 /* PCH Flow Control Refresh Timer Value */ @@ -201,6 +203,15 @@ #define SW_FLAG_TIMEOUT 1000 /* SW Semaphore flag timeout in ms */ +/* Inband Control */ +#define I217_INBAND_CTRL PHY_REG(770, 18) +#define I217_INBAND_CTRL_LINK_STAT_TX_TIMEOUT_MASK 0x3F00 +#define I217_INBAND_CTRL_LINK_STAT_TX_TIMEOUT_SHIFT 8 + +/* Low Power Idle GPIO Control */ +#define I217_LPI_GPIO_CTRL PHY_REG(772, 18) +#define I217_LPI_GPIO_CTRL_AUTO_EN_LPI 0x0800 + /* PHY Low Power Idle Control */ #define I82579_LPI_CTRL PHY_REG(772, 20) #define I82579_LPI_CTRL_100_ENABLE 0x2000 @@ -208,6 +219,10 @@ #define I82579_LPI_CTRL_ENABLE_MASK 0x6000 #define I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT 0x80 +/* 82579 DFT Control */ +#define I82579_DFT_CTRL PHY_REG(769, 20) +#define I82579_DFT_CTRL_GATE_PHY_RESET 0x0040 /* Gate PHY Reset on MAC Reset */ + /* Extended Management Interface (EMI) Registers */ #define I82579_EMI_ADDR 0x10 #define I82579_EMI_DATA 0x11 @@ -216,12 +231,12 @@ #define I82577_MSE_THRESHOLD 0x0887 /* 82577 Mean Square Error Threshold */ #define I82579_MSE_LINK_DOWN 0x2411 /* MSE count before dropping link */ #define I82579_RX_CONFIG 0x3412 /* Receive configuration */ -#define I82579_EEE_PCS_STATUS 0x182D /* IEEE MMD Register 3.1 >> 8 */ +#define I82579_EEE_PCS_STATUS 0x182E /* IEEE MMD Register 3.1 >> 8 */ #define I82579_EEE_CAPABILITY 0x0410 /* IEEE MMD Register 3.20 */ #define I82579_EEE_ADVERTISEMENT 0x040E /* IEEE MMD Register 7.60 */ #define I82579_EEE_LP_ABILITY 0x040F /* IEEE MMD Register 7.61 */ -#define I82579_EEE_100_SUPPORTED (1 << 1) /* 100BaseTx EEE supported */ -#define I82579_EEE_1000_SUPPORTED (1 << 2) /* 1000BaseTx EEE supported */ +#define I82579_EEE_100_SUPPORTED (1 << 1) /* 100BaseTx EEE */ +#define I82579_EEE_1000_SUPPORTED (1 << 2) /* 1000BaseTx EEE */ #define I217_EEE_PCS_STATUS 0x9401 /* IEEE MMD Register 3.1 */ #define I217_EEE_CAPABILITY 0x8000 /* IEEE MMD Register 3.20 */ #define I217_EEE_ADVERTISEMENT 0x8001 /* IEEE MMD Register 7.60 */ @@ -274,4 +289,7 @@ s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable); void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw); s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable); s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data); +s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data); +s32 e1000_set_eee_pchlan(struct e1000_hw *hw); +void e1000_toggle_lanphypc_pch_lpt(struct e1000_hw *hw); #endif /* _E1000_ICH8LAN_H_ */ diff --git a/sys/dev/e1000/e1000_mac.c b/sys/dev/e1000/e1000_mac.c index 9e6b30c49f5..cba3feaf862 100644 --- a/sys/dev/e1000/e1000_mac.c +++ b/sys/dev/e1000/e1000_mac.c @@ -85,7 +85,7 @@ void e1000_init_mac_ops_generic(struct e1000_hw *hw) * e1000_null_ops_generic - No-op function, returns 0 * @hw: pointer to the HW structure **/ -s32 e1000_null_ops_generic(struct e1000_hw *hw) +s32 e1000_null_ops_generic(struct e1000_hw E1000_UNUSEDARG *hw) { DEBUGFUNC("e1000_null_ops_generic"); return E1000_SUCCESS; @@ -95,7 +95,7 @@ s32 e1000_null_ops_generic(struct e1000_hw *hw) * e1000_null_mac_generic - No-op function, return void * @hw: pointer to the HW structure **/ -void e1000_null_mac_generic(struct e1000_hw *hw) +void e1000_null_mac_generic(struct e1000_hw E1000_UNUSEDARG *hw) { DEBUGFUNC("e1000_null_mac_generic"); return; @@ -105,7 +105,8 @@ void e1000_null_mac_generic(struct e1000_hw *hw) * e1000_null_link_info - No-op function, return 0 * @hw: pointer to the HW structure **/ -s32 e1000_null_link_info(struct e1000_hw *hw, u16 *s, u16 *d) +s32 e1000_null_link_info(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG *s, u16 E1000_UNUSEDARG *d) { DEBUGFUNC("e1000_null_link_info"); return E1000_SUCCESS; @@ -115,7 +116,8 @@ s32 e1000_null_link_info(struct e1000_hw *hw, u16 *s, u16 *d) * e1000_null_mng_mode - No-op function, return FALSE * @hw: pointer to the HW structure **/ -bool e1000_null_mng_mode(struct e1000_hw *hw) { +bool e1000_null_mng_mode(struct e1000_hw E1000_UNUSEDARG *hw) +{ DEBUGFUNC("e1000_null_mng_mode"); return FALSE; } @@ -124,7 +126,8 @@ bool e1000_null_mng_mode(struct e1000_hw *hw) { * e1000_null_update_mc - No-op function, return void * @hw: pointer to the HW structure **/ -void e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a) +void e1000_null_update_mc(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG *h, u32 E1000_UNUSEDARG a) { DEBUGFUNC("e1000_null_update_mc"); return; @@ -134,7 +137,8 @@ void e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a) * e1000_null_write_vfta - No-op function, return void * @hw: pointer to the HW structure **/ -void e1000_null_write_vfta(struct e1000_hw *hw, u32 a, u32 b) +void e1000_null_write_vfta(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG a, u32 E1000_UNUSEDARG b) { DEBUGFUNC("e1000_null_write_vfta"); return; @@ -144,7 +148,8 @@ void e1000_null_write_vfta(struct e1000_hw *hw, u32 a, u32 b) * e1000_null_rar_set - No-op function, return void * @hw: pointer to the HW structure **/ -void e1000_null_rar_set(struct e1000_hw *hw, u8 *h, u32 a) +void e1000_null_rar_set(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG *h, u32 E1000_UNUSEDARG a) { DEBUGFUNC("e1000_null_rar_set"); return; @@ -154,7 +159,8 @@ void e1000_null_rar_set(struct e1000_hw *hw, u8 *h, u32 a) * e1000_null_set_obff_timer - No-op function, return 0 * @hw: pointer to the HW structure **/ -s32 e1000_null_set_obff_timer(struct e1000_hw *hw, u32 a) +s32 e1000_null_set_obff_timer(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG a) { DEBUGFUNC("e1000_null_set_obff_timer"); return E1000_SUCCESS; @@ -940,6 +946,7 @@ s32 e1000_set_default_fc_generic(struct e1000_hw *hw) { s32 ret_val; u16 nvm_data; + u16 nvm_offset = 0; DEBUGFUNC("e1000_set_default_fc_generic"); @@ -951,7 +958,18 @@ s32 e1000_set_default_fc_generic(struct e1000_hw *hw) * control setting, then the variable hw->fc will * be initialized based on a value in the EEPROM. */ - ret_val = hw->nvm.ops.read(hw, NVM_INIT_CONTROL2_REG, 1, &nvm_data); + if (hw->mac.type == e1000_i350) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(hw->bus.func); + ret_val = hw->nvm.ops.read(hw, + NVM_INIT_CONTROL2_REG + + nvm_offset, + 1, &nvm_data); + } else { + ret_val = hw->nvm.ops.read(hw, + NVM_INIT_CONTROL2_REG, + 1, &nvm_data); + } + if (ret_val) { DEBUGOUT("NVM Read Error\n"); @@ -1675,7 +1693,7 @@ s32 e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed, * Sets the speed and duplex to gigabit full duplex (the only possible option) * for fiber/serdes links. **/ -s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw *hw, +s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw E1000_UNUSEDARG *hw, u16 *speed, u16 *duplex) { DEBUGFUNC("e1000_get_speed_and_duplex_fiber_serdes_generic"); @@ -2187,7 +2205,7 @@ static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw) * Validate the MDI/MDIx setting, allowing for auto-crossover during forced * operation. **/ -s32 e1000_validate_mdi_setting_crossover_generic(struct e1000_hw *hw) +s32 e1000_validate_mdi_setting_crossover_generic(struct e1000_hw E1000_UNUSEDARG *hw) { DEBUGFUNC("e1000_validate_mdi_setting_crossover_generic"); diff --git a/sys/dev/e1000/e1000_osdep.h b/sys/dev/e1000/e1000_osdep.h index dd19613e0b3..9cf710e232a 100644 --- a/sys/dev/e1000/e1000_osdep.h +++ b/sys/dev/e1000/e1000_osdep.h @@ -60,6 +60,7 @@ #define ASSERT(x) if(!(x)) panic("EM: x") #define usec_delay(x) DELAY(x) +#define usec_delay_irq(x) DELAY(x) #define msec_delay(x) DELAY(1000*(x)) #define msec_delay_irq(x) DELAY(1000*(x)) diff --git a/sys/dev/e1000/e1000_phy.c b/sys/dev/e1000/e1000_phy.c index 241c1d5d307..38204109b98 100644 --- a/sys/dev/e1000/e1000_phy.c +++ b/sys/dev/e1000/e1000_phy.c @@ -104,7 +104,8 @@ void e1000_init_phy_ops_generic(struct e1000_hw *hw) * e1000_null_set_page - No-op function, return 0 * @hw: pointer to the HW structure **/ -s32 e1000_null_set_page(struct e1000_hw *hw, u16 data) +s32 e1000_null_set_page(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG data) { DEBUGFUNC("e1000_null_set_page"); return E1000_SUCCESS; @@ -114,7 +115,8 @@ s32 e1000_null_set_page(struct e1000_hw *hw, u16 data) * e1000_null_read_reg - No-op function, return 0 * @hw: pointer to the HW structure **/ -s32 e1000_null_read_reg(struct e1000_hw *hw, u32 offset, u16 *data) +s32 e1000_null_read_reg(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG offset, u16 E1000_UNUSEDARG *data) { DEBUGFUNC("e1000_null_read_reg"); return E1000_SUCCESS; @@ -124,7 +126,7 @@ s32 e1000_null_read_reg(struct e1000_hw *hw, u32 offset, u16 *data) * e1000_null_phy_generic - No-op function, return void * @hw: pointer to the HW structure **/ -void e1000_null_phy_generic(struct e1000_hw *hw) +void e1000_null_phy_generic(struct e1000_hw E1000_UNUSEDARG *hw) { DEBUGFUNC("e1000_null_phy_generic"); return; @@ -134,7 +136,8 @@ void e1000_null_phy_generic(struct e1000_hw *hw) * e1000_null_lplu_state - No-op function, return 0 * @hw: pointer to the HW structure **/ -s32 e1000_null_lplu_state(struct e1000_hw *hw, bool active) +s32 e1000_null_lplu_state(struct e1000_hw E1000_UNUSEDARG *hw, + bool E1000_UNUSEDARG active) { DEBUGFUNC("e1000_null_lplu_state"); return E1000_SUCCESS; @@ -144,7 +147,8 @@ s32 e1000_null_lplu_state(struct e1000_hw *hw, bool active) * e1000_null_write_reg - No-op function, return 0 * @hw: pointer to the HW structure **/ -s32 e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data) +s32 e1000_null_write_reg(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG offset, u16 E1000_UNUSEDARG data) { DEBUGFUNC("e1000_null_write_reg"); return E1000_SUCCESS; @@ -158,8 +162,10 @@ s32 e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data) * @data: data value read * **/ -s32 e1000_read_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, - u8 dev_addr, u8 *data) +s32 e1000_read_i2c_byte_null(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG byte_offset, + u8 E1000_UNUSEDARG dev_addr, + u8 E1000_UNUSEDARG *data) { DEBUGFUNC("e1000_read_i2c_byte_null"); return E1000_SUCCESS; @@ -173,10 +179,10 @@ s32 e1000_read_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, * @data: data value to write * **/ -s32 e1000_write_i2c_byte_null(struct e1000_hw *hw, - u8 byte_offset, - u8 dev_addr, - u8 data) +s32 e1000_write_i2c_byte_null(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG byte_offset, + u8 E1000_UNUSEDARG dev_addr, + u8 E1000_UNUSEDARG data) { DEBUGFUNC("e1000_write_i2c_byte_null"); return E1000_SUCCESS; @@ -302,7 +308,7 @@ s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) * the lower time out */ for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { - usec_delay(50); + usec_delay_irq(50); mdic = E1000_READ_REG(hw, E1000_MDIC); if (mdic & E1000_MDIC_READY) break; @@ -327,7 +333,7 @@ s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) * reading duplicate data in the next MDIC transaction. */ if (hw->mac.type == e1000_pch2lan) - usec_delay(100); + usec_delay_irq(100); return E1000_SUCCESS; } @@ -368,7 +374,7 @@ s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) * the lower time out */ for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { - usec_delay(50); + usec_delay_irq(50); mdic = E1000_READ_REG(hw, E1000_MDIC); if (mdic & E1000_MDIC_READY) break; @@ -392,7 +398,7 @@ s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) * reading duplicate data in the next MDIC transaction. */ if (hw->mac.type == e1000_pch2lan) - usec_delay(100); + usec_delay_irq(100); return E1000_SUCCESS; } @@ -1054,16 +1060,12 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) } } - /* Enable CRS on Tx. This must be set for half-duplex operation. - * Not required on some PHYs. - */ + /* Enable CRS on Tx. This must be set for half-duplex operation. */ ret_val = hw->phy.ops.read_reg(hw, I82577_CFG_REG, &phy_data); if (ret_val) return ret_val; - if ((hw->phy.type != e1000_phy_82579) && - (hw->phy.type != e1000_phy_i217)) - phy_data |= I82577_CFG_ASSERT_CRS_ON_TX; + phy_data |= I82577_CFG_ASSERT_CRS_ON_TX; /* Enable downshift */ phy_data |= I82577_CFG_ENABLE_DOWNSHIFT; @@ -1249,12 +1251,6 @@ s32 e1000_copper_link_setup_m88(struct e1000_hw *hw) return ret_val; } - if (phy->type == e1000_phy_i210) { - ret_val = e1000_set_master_slave_mode(hw); - if (ret_val) - return ret_val; - } - return E1000_SUCCESS; } @@ -1318,6 +1314,20 @@ s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw) phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; /* Enable downshift and setting it to X6 */ + if (phy->id == M88E1543_E_PHY_ID) { + phy_data &= ~I347AT4_PSCR_DOWNSHIFT_ENABLE; + ret_val = + phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + } + phy_data &= ~I347AT4_PSCR_DOWNSHIFT_MASK; phy_data |= I347AT4_PSCR_DOWNSHIFT_6X; phy_data |= I347AT4_PSCR_DOWNSHIFT_ENABLE; @@ -1333,6 +1343,10 @@ s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw) return ret_val; } + ret_val = e1000_set_master_slave_mode(hw); + if (ret_val) + return ret_val; + return E1000_SUCCESS; } @@ -1847,6 +1861,8 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) case I347AT4_E_PHY_ID: case M88E1340M_E_PHY_ID: case M88E1112_E_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: case I210_I_PHY_ID: reset_dsp = FALSE; break; @@ -1889,6 +1905,9 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) return E1000_SUCCESS; if (hw->phy.id == I210_I_PHY_ID) return E1000_SUCCESS; + if ((hw->phy.id == M88E1543_E_PHY_ID) || + (hw->phy.id == M88E1512_E_PHY_ID)) + return E1000_SUCCESS; ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); if (ret_val) return ret_val; @@ -2194,9 +2213,9 @@ s32 e1000_check_polarity_m88(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &data); if (!ret_val) - phy->cable_polarity = (data & M88E1000_PSSR_REV_POLARITY) - ? e1000_rev_polarity_reversed - : e1000_rev_polarity_normal; + phy->cable_polarity = ((data & M88E1000_PSSR_REV_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); return ret_val; } @@ -2240,9 +2259,9 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, offset, &data); if (!ret_val) - phy->cable_polarity = (data & mask) - ? e1000_rev_polarity_reversed - : e1000_rev_polarity_normal; + phy->cable_polarity = ((data & mask) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); return ret_val; } @@ -2274,9 +2293,9 @@ s32 e1000_check_polarity_ife(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, offset, &phy_data); if (!ret_val) - phy->cable_polarity = (phy_data & mask) + phy->cable_polarity = ((phy_data & mask) ? e1000_rev_polarity_reversed - : e1000_rev_polarity_normal; + : e1000_rev_polarity_normal); return ret_val; } @@ -2392,8 +2411,8 @@ s32 e1000_get_cable_length_m88(struct e1000_hw *hw) if (ret_val) return ret_val; - index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> - M88E1000_PSSR_CABLE_LENGTH_SHIFT; + index = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT); if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) return -E1000_ERR_PHY; @@ -2437,6 +2456,8 @@ s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw) phy->max_cable_length = phy_data / (is_cm ? 100 : 1); phy->cable_length = phy_data / (is_cm ? 100 : 1); break; + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: case M88E1340M_E_PHY_ID: case I347AT4_E_PHY_ID: /* Remember the original page select and set it to 7 */ @@ -2554,8 +2575,8 @@ s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw) * that can be put into the lookup table to obtain the * approximate cable length. */ - cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & - IGP02E1000_AGC_LENGTH_MASK; + cur_agc_index = ((phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & + IGP02E1000_AGC_LENGTH_MASK); /* Array index bound check. */ if ((cur_agc_index >= IGP02E1000_CABLE_LENGTH_TABLE_SIZE) || @@ -2578,8 +2599,8 @@ s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw) agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2); /* Calculate cable length with the error range of +/- 10 meters. */ - phy->min_cable_length = ((agc_value - IGP02E1000_AGC_RANGE) > 0) ? - (agc_value - IGP02E1000_AGC_RANGE) : 0; + phy->min_cable_length = (((agc_value - IGP02E1000_AGC_RANGE) > 0) ? + (agc_value - IGP02E1000_AGC_RANGE) : 0); phy->max_cable_length = agc_value + IGP02E1000_AGC_RANGE; phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; @@ -2763,9 +2784,9 @@ s32 e1000_get_phy_info_ife(struct e1000_hw *hw) return ret_val; } else { /* Polarity is forced */ - phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY) - ? e1000_rev_polarity_reversed - : e1000_rev_polarity_normal; + phy->cable_polarity = ((data & IFE_PSC_FORCE_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); } ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data); @@ -2863,7 +2884,7 @@ s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw) * Generic function to wait 10 milli-seconds for configuration to complete * and return success. **/ -s32 e1000_get_cfg_done_generic(struct e1000_hw *hw) +s32 e1000_get_cfg_done_generic(struct e1000_hw E1000_UNUSEDARG *hw) { DEBUGFUNC("e1000_get_cfg_done_generic"); @@ -2970,6 +2991,8 @@ enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id) case M88E1000_E_PHY_ID: case M88E1111_I_PHY_ID: case M88E1011_I_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: case I347AT4_E_PHY_ID: case M88E1112_E_PHY_ID: case M88E1340M_E_PHY_ID: @@ -3776,8 +3799,8 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, DEBUGFUNC("e1000_access_phy_debug_regs_hv"); /* This takes care of the difference with desktop vs mobile phy */ - addr_reg = (hw->phy.type == e1000_phy_82578) ? - I82578_ADDR_REG : I82577_ADDR_REG; + addr_reg = ((hw->phy.type == e1000_phy_82578) ? + I82578_ADDR_REG : I82577_ADDR_REG); data_reg = addr_reg + 1; /* All operations in this function are phy address 2 */ @@ -3833,8 +3856,8 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw) if (ret_val) return ret_val; - data &= BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED | - BM_CS_STATUS_SPEED_MASK; + data &= (BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED | + BM_CS_STATUS_SPEED_MASK); if (data != (BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED | BM_CS_STATUS_SPEED_1000)) @@ -3872,9 +3895,9 @@ s32 e1000_check_polarity_82577(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data); if (!ret_val) - phy->cable_polarity = (data & I82577_PHY_STATUS2_REV_POLARITY) - ? e1000_rev_polarity_reversed - : e1000_rev_polarity_normal; + phy->cable_polarity = ((data & I82577_PHY_STATUS2_REV_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); return ret_val; } @@ -4009,8 +4032,8 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw) if (ret_val) return ret_val; - length = (phy_data & I82577_DSTATUS_CABLE_LENGTH) >> - I82577_DSTATUS_CABLE_LENGTH_SHIFT; + length = ((phy_data & I82577_DSTATUS_CABLE_LENGTH) >> + I82577_DSTATUS_CABLE_LENGTH_SHIFT); if (length == E1000_CABLE_LENGTH_UNDEFINED) return -E1000_ERR_PHY; @@ -4083,3 +4106,157 @@ release: return ret_val; } +/** + * e1000_read_phy_reg_mphy - Read mPHY control register + * @hw: pointer to the HW structure + * @address: address to be read + * @data: pointer to the read data + * + * Reads the mPHY control register in the PHY at offset and stores the + * information read to data. + **/ +s32 e1000_read_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 *data) +{ + u32 mphy_ctrl = 0; + bool locked = FALSE; + bool ready = FALSE; + + DEBUGFUNC("e1000_read_phy_reg_mphy"); + + /* Check if mPHY is ready to read/write operations */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* Check if mPHY access is disabled and enable it if so */ + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_DIS_ACCESS) { + locked = TRUE; + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + mphy_ctrl |= E1000_MPHY_ENA_ACCESS; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + } + + /* Set the address that we want to read */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* We mask address, because we want to use only current lane */ + mphy_ctrl = (mphy_ctrl & ~E1000_MPHY_ADDRESS_MASK & + ~E1000_MPHY_ADDRESS_FNC_OVERRIDE) | + (address & E1000_MPHY_ADDRESS_MASK); + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + + /* Read data from the address */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + *data = E1000_READ_REG(hw, E1000_MPHY_DATA); + + /* Disable access to mPHY if it was originally disabled */ + if (locked) + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, + E1000_MPHY_DIS_ACCESS); + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_mphy - Write mPHY control register + * @hw: pointer to the HW structure + * @address: address to write to + * @data: data to write to register at offset + * @line_override: used when we want to use different line than default one + * + * Writes data to mPHY control register. + **/ +s32 e1000_write_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 data, + bool line_override) +{ + u32 mphy_ctrl = 0; + bool locked = FALSE; + bool ready = FALSE; + + DEBUGFUNC("e1000_write_phy_reg_mphy"); + + /* Check if mPHY is ready to read/write operations */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* Check if mPHY access is disabled and enable it if so */ + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_DIS_ACCESS) { + locked = TRUE; + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + mphy_ctrl |= E1000_MPHY_ENA_ACCESS; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + } + + /* Set the address that we want to read */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* We mask address, because we want to use only current lane */ + if (line_override) + mphy_ctrl |= E1000_MPHY_ADDRESS_FNC_OVERRIDE; + else + mphy_ctrl &= ~E1000_MPHY_ADDRESS_FNC_OVERRIDE; + mphy_ctrl = (mphy_ctrl & ~E1000_MPHY_ADDRESS_MASK) | + (address & E1000_MPHY_ADDRESS_MASK); + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + + /* Read data from the address */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_DATA, data); + + /* Disable access to mPHY if it was originally disabled */ + if (locked) + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, + E1000_MPHY_DIS_ACCESS); + + return E1000_SUCCESS; +} + +/** + * e1000_is_mphy_ready - Check if mPHY control register is not busy + * @hw: pointer to the HW structure + * + * Returns mPHY control register status. + **/ +bool e1000_is_mphy_ready(struct e1000_hw *hw) +{ + u16 retry_count = 0; + u32 mphy_ctrl = 0; + bool ready = FALSE; + + while (retry_count < 2) { + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_BUSY) { + usec_delay(20); + retry_count++; + continue; + } + ready = TRUE; + break; + } + + if (!ready) + DEBUGOUT("ERROR READING mPHY control register, phy is busy.\n"); + + return ready; +} diff --git a/sys/dev/e1000/e1000_phy.h b/sys/dev/e1000/e1000_phy.h index 9911df772d9..32685eeee81 100644 --- a/sys/dev/e1000/e1000_phy.h +++ b/sys/dev/e1000/e1000_phy.h @@ -116,6 +116,10 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw); s32 e1000_get_cable_length_82577(struct e1000_hw *hw); s32 e1000_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data); s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 *data); +s32 e1000_write_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 data, + bool line_override); +bool e1000_is_mphy_ready(struct e1000_hw *hw); #define E1000_MAX_PHY_ADDR 8 @@ -170,7 +174,7 @@ s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); #define I82577_ADDR_REG 16 #define I82577_CFG_REG 22 #define I82577_CFG_ASSERT_CRS_ON_TX (1 << 15) -#define I82577_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift 100/10 */ +#define I82577_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift */ #define I82577_CTRL_REG 23 /* 82577 specific PHY registers */ @@ -201,6 +205,12 @@ s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); #define E1000_82580_PM_D3_LPLU 0x0004 /* For all other states */ #define E1000_82580_PM_GO_LINKD 0x0020 /* Go Link Disconnect */ +#define E1000_MPHY_DIS_ACCESS 0x80000000 /* disable_access bit */ +#define E1000_MPHY_ENA_ACCESS 0x40000000 /* enable_access bit */ +#define E1000_MPHY_BUSY 0x00010000 /* busy bit */ +#define E1000_MPHY_ADDRESS_FNC_OVERRIDE 0x20000000 /* fnc_override bit */ +#define E1000_MPHY_ADDRESS_MASK 0x0000FFFF /* address mask */ + /* BM PHY Copper Specific Control 1 */ #define BM_CS_CTRL1 16 @@ -247,7 +257,7 @@ s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); #define IGP02E1000_PHY_AGC_C 0x14B1 #define IGP02E1000_PHY_AGC_D 0x18B1 -#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Course - 15:13, Fine - 12:9 */ +#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Course=15:13, Fine=12:9 */ #define IGP02E1000_AGC_LENGTH_MASK 0x7F #define IGP02E1000_AGC_RANGE 15 @@ -267,8 +277,8 @@ s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); #define E1000_KMRNCTRLSTA_HD_CTRL 0x10 /* Kumeran HD Control */ #define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 -#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY Special Control */ -#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY Special and LED Control */ +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY Special Ctrl */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY Special and LED Ctrl */ #define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control */ /* IFE PHY Extended Status Control */ diff --git a/sys/dev/e1000/e1000_regs.h b/sys/dev/e1000/e1000_regs.h index 516d377af40..f2b37db0a62 100644 --- a/sys/dev/e1000/e1000_regs.h +++ b/sys/dev/e1000/e1000_regs.h @@ -50,6 +50,10 @@ #define E1000_BARCTRL 0x5BBC /* BAR ctrl reg */ #define E1000_BARCTRL_FLSIZE 0x0700 /* BAR ctrl Flsize */ #define E1000_BARCTRL_CSRSIZE 0x2000 /* BAR ctrl CSR size */ +#define E1000_MPHY_ADDR_CTRL 0x0024 /* GbE MPHY Address Control */ +#define E1000_MPHY_DATA 0x0E10 /* GBE MPHY Data */ +#define E1000_MPHY_STAT 0x0E0C /* GBE MPHY Statistics */ +#define E1000_PPHY_CTRL 0x5b48 /* PCIe PHY Control */ #define E1000_I350_BARCTRL 0x5BFC /* BAR ctrl reg */ #define E1000_I350_DTXMXPKTSZ 0x355C /* Maximum sent packet size reg*/ #define E1000_SCTL 0x00024 /* SerDes Control - RW */ @@ -94,6 +98,7 @@ #define E1000_TBT 0x00448 /* Tx Burst Timer - RW */ #define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ #define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_LEDMUX 0x08130 /* LED MUX Control */ #define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ #define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ #define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ @@ -152,6 +157,8 @@ #define E1000_PBRWAC 0x024E8 /* Rx packet buffer wrap around counter - RO */ #define E1000_RDTR 0x02820 /* Rx Delay Timer - RW */ #define E1000_RADV 0x0282C /* Rx Interrupt Absolute Delay Timer - RW */ +#define E1000_EMIADD 0x10 /* Extended Memory Indirect Address */ +#define E1000_EMIDATA 0x11 /* Extended Memory Indirect Data */ #define E1000_SRWR 0x12018 /* Shadow Ram Write Register - RW */ #define E1000_I210_FLMNGCTL 0x12038 #define E1000_I210_FLMNGDATA 0x1203C @@ -208,6 +215,9 @@ /* Queues packet buffer size masks where _n can be 0-3 and _s 0-63 [kB] */ #define E1000_I210_TXPBS_SIZE(_n, _s) ((_s) << (6 * _n)) +#define E1000_MMDAC 13 /* MMD Access Control */ +#define E1000_MMDAAD 14 /* MMD Access Address/Data */ + /* Convenience macros * * Note: "_n" is the queue number of the register to be written to. @@ -484,8 +494,6 @@ #define E1000_PBACL 0x05B68 /* MSIx PBA Clear - Read/Write 1's to clear */ #define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ #define E1000_HOST_IF 0x08800 /* Host Interface */ -#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ -#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ #define E1000_HIBBA 0x8F40 /* Host Interface Buffer Base Address */ /* Flexible Host Filter Table */ #define E1000_FHFT(_n) (0x09000 + ((_n) * 0x100)) diff --git a/sys/dev/e1000/e1000_vf.h b/sys/dev/e1000/e1000_vf.h index 0ee73aec628..4dfebe7a4bd 100644 --- a/sys/dev/e1000/e1000_vf.h +++ b/sys/dev/e1000/e1000_vf.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2010, Intel Corporation + Copyright (c) 2001-2013, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -41,48 +41,50 @@ struct e1000_hw; -#define E1000_DEV_ID_82576_VF 0x10CA -#define E1000_DEV_ID_I350_VF 0x1520 +#define E1000_DEV_ID_82576_VF 0x10CA +#define E1000_DEV_ID_I350_VF 0x1520 -#define E1000_VF_INIT_TIMEOUT 200 /* Number of retries to clear RSTI */ +#define E1000_VF_INIT_TIMEOUT 200 /* Num of retries to clear RSTI */ /* Additional Descriptor Control definitions */ -#define E1000_TXDCTL_QUEUE_ENABLE 0x02000000 /* Enable specific Tx Queue */ -#define E1000_RXDCTL_QUEUE_ENABLE 0x02000000 /* Enable specific Rx Queue */ +#define E1000_TXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Tx Queue */ +#define E1000_RXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Rx Queue */ /* SRRCTL bit definitions */ -#define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */ -#define E1000_SRRCTL_BSIZEHDRSIZE_MASK 0x00000F00 -#define E1000_SRRCTL_BSIZEHDRSIZE_SHIFT 2 /* Shift _left_ */ -#define E1000_SRRCTL_DESCTYPE_LEGACY 0x00000000 -#define E1000_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000 -#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT 0x04000000 -#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000 -#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION 0x06000000 +#define E1000_SRRCTL(_n) ((_n) < 4 ? (0x0280C + ((_n) * 0x100)) : \ + (0x0C00C + ((_n) * 0x40))) +#define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */ +#define E1000_SRRCTL_BSIZEHDRSIZE_MASK 0x00000F00 +#define E1000_SRRCTL_BSIZEHDRSIZE_SHIFT 2 /* Shift _left_ */ +#define E1000_SRRCTL_DESCTYPE_LEGACY 0x00000000 +#define E1000_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT 0x04000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000 +#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION 0x06000000 #define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION_LARGE_PKT 0x08000000 -#define E1000_SRRCTL_DESCTYPE_MASK 0x0E000000 -#define E1000_SRRCTL_DROP_EN 0x80000000 +#define E1000_SRRCTL_DESCTYPE_MASK 0x0E000000 +#define E1000_SRRCTL_DROP_EN 0x80000000 -#define E1000_SRRCTL_BSIZEPKT_MASK 0x0000007F -#define E1000_SRRCTL_BSIZEHDR_MASK 0x00003F00 +#define E1000_SRRCTL_BSIZEPKT_MASK 0x0000007F +#define E1000_SRRCTL_BSIZEHDR_MASK 0x00003F00 /* Interrupt Defines */ -#define E1000_EICR 0x01580 /* Ext. Interrupt Cause Read - R/clr */ -#define E1000_EITR(_n) (0x01680 + ((_n) << 2)) -#define E1000_EICS 0x01520 /* Ext. Interrupt Cause Set - W0 */ -#define E1000_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */ -#define E1000_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */ -#define E1000_EIAC 0x0152C /* Ext. Interrupt Auto Clear - RW */ -#define E1000_EIAM 0x01530 /* Ext. Interrupt Ack Auto Clear Mask - RW */ -#define E1000_IVAR0 0x01700 /* Interrupt Vector Allocation (array) - RW */ -#define E1000_IVAR_MISC 0x01740 /* IVAR for "other" causes - RW */ -#define E1000_IVAR_VALID 0x80 +#define E1000_EICR 0x01580 /* Ext. Interrupt Cause Read - R/clr */ +#define E1000_EITR(_n) (0x01680 + ((_n) << 2)) +#define E1000_EICS 0x01520 /* Ext. Intr Cause Set -W0 */ +#define E1000_EIMS 0x01524 /* Ext. Intr Mask Set/Read -RW */ +#define E1000_EIMC 0x01528 /* Ext. Intr Mask Clear -WO */ +#define E1000_EIAC 0x0152C /* Ext. Intr Auto Clear -RW */ +#define E1000_EIAM 0x01530 /* Ext. Intr Ack Auto Clear Mask -RW */ +#define E1000_IVAR0 0x01700 /* Intr Vector Alloc (array) -RW */ +#define E1000_IVAR_MISC 0x01740 /* IVAR for "other" causes -RW */ +#define E1000_IVAR_VALID 0x80 /* Receive Descriptor - Advanced */ union e1000_adv_rx_desc { struct { - u64 pkt_addr; /* Packet buffer address */ - u64 hdr_addr; /* Header buffer address */ + u64 pkt_addr; /* Packet buffer address */ + u64 hdr_addr; /* Header buffer address */ } read; struct { struct { @@ -96,23 +98,23 @@ union e1000_adv_rx_desc { } hs_rss; } lo_dword; union { - u32 rss; /* RSS Hash */ + u32 rss; /* RSS Hash */ struct { - u16 ip_id; /* IP id */ - u16 csum; /* Packet Checksum */ + u16 ip_id; /* IP id */ + u16 csum; /* Packet Checksum */ } csum_ip; } hi_dword; } lower; struct { - u32 status_error; /* ext status/error */ - u16 length; /* Packet length */ - u16 vlan; /* VLAN tag */ + u32 status_error; /* ext status/error */ + u16 length; /* Packet length */ + u16 vlan; /* VLAN tag */ } upper; } wb; /* writeback */ }; -#define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0 -#define E1000_RXDADV_HDRBUFLEN_SHIFT 5 +#define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0 +#define E1000_RXDADV_HDRBUFLEN_SHIFT 5 /* Transmit Descriptor - Advanced */ union e1000_adv_tx_desc { @@ -129,15 +131,15 @@ union e1000_adv_tx_desc { }; /* Adv Transmit Descriptor Config Masks */ -#define E1000_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Descriptor */ -#define E1000_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */ -#define E1000_ADVTXD_DCMD_EOP 0x01000000 /* End of Packet */ -#define E1000_ADVTXD_DCMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ -#define E1000_ADVTXD_DCMD_RS 0x08000000 /* Report Status */ -#define E1000_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */ -#define E1000_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */ -#define E1000_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */ -#define E1000_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */ +#define E1000_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Descriptor */ +#define E1000_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */ +#define E1000_ADVTXD_DCMD_EOP 0x01000000 /* End of Packet */ +#define E1000_ADVTXD_DCMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_ADVTXD_DCMD_RS 0x08000000 /* Report Status */ +#define E1000_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */ +#define E1000_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */ +#define E1000_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */ +#define E1000_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */ /* Context descriptors */ struct e1000_adv_tx_context_desc { @@ -147,11 +149,11 @@ struct e1000_adv_tx_context_desc { u32 mss_l4len_idx; }; -#define E1000_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */ -#define E1000_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type: 1=IPv4 */ -#define E1000_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */ -#define E1000_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */ -#define E1000_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */ +#define E1000_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */ +#define E1000_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type: 1=IPv4 */ +#define E1000_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */ +#define E1000_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */ +#define E1000_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */ enum e1000_mac_type { e1000_undefined = 0, diff --git a/sys/dev/e1000/if_igb.c b/sys/dev/e1000/if_igb.c index 600c2de28d3..07a9d3e76db 100644 --- a/sys/dev/e1000/if_igb.c +++ b/sys/dev/e1000/if_igb.c @@ -101,7 +101,7 @@ int igb_display_debug_stats = 0; /********************************************************************* * Driver version: *********************************************************************/ -char igb_driver_version[] = "version - 2.3.10"; +char igb_driver_version[] = "version - 2.4.0"; /********************************************************************* @@ -155,10 +155,19 @@ static igb_vendor_info_t igb_vendor_info_array[] = { 0x8086, E1000_DEV_ID_I210_COPPER_IT, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, E1000_DEV_ID_I210_COPPER_OEM1, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I210_COPPER_FLASHLESS, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I210_SERDES_FLASHLESS, + PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, E1000_DEV_ID_I210_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, E1000_DEV_ID_I210_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, E1000_DEV_ID_I210_SGMII, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, E1000_DEV_ID_I211_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I354_BACKPLANE_1GBPS, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I354_SGMII, PCI_ANY_ID, PCI_ANY_ID, 0}, /* required last entry */ { 0, 0, 0, 0, 0} }; @@ -233,9 +242,10 @@ static __inline void igb_rx_input(struct rx_ring *, static bool igb_rxeof(struct igb_queue *, int, int *); static void igb_rx_checksum(u32, struct mbuf *, u32); -static bool igb_tx_ctx_setup(struct tx_ring *, struct mbuf *); -static bool igb_tso_setup(struct tx_ring *, struct mbuf *, int, - struct ip *, struct tcphdr *); +static int igb_tx_ctx_setup(struct tx_ring *, + struct mbuf *, u32 *, u32 *); +static int igb_tso_setup(struct tx_ring *, + struct mbuf *, u32 *, u32 *); static void igb_set_promisc(struct adapter *); static void igb_disable_promisc(struct adapter *); static void igb_set_multi(struct adapter *); @@ -351,7 +361,7 @@ TUNABLE_INT("hw.igb.max_interrupt_rate", &igb_max_interrupt_rate); SYSCTL_INT(_hw_igb, OID_AUTO, max_interrupt_rate, CTLFLAG_RDTUN, &igb_max_interrupt_rate, 0, "Maximum interrupts per second"); -#if __FreeBSD_version >= 800000 +#ifndef IGB_LEGACY_TX /* ** Tuneable number of buffers in the buf-ring (drbr_xxx) */ @@ -557,7 +567,6 @@ igb_attach(device_t dev) * standard ethernet sized frames. */ adapter->max_frame_size = ETHERMTU + ETHER_HDR_LEN + ETHERNET_FCS_SIZE; - adapter->min_frame_size = ETH_ZLEN + ETHERNET_FCS_SIZE; /* ** Allocate and Setup Queues @@ -603,8 +612,12 @@ igb_attach(device_t dev) OID_AUTO, "eee_disabled", CTLTYPE_INT|CTLFLAG_RW, adapter, 0, igb_sysctl_eee, "I", "Disable Energy Efficient Ethernet"); - if (adapter->hw.phy.media_type == e1000_media_type_copper) - e1000_set_eee_i350(&adapter->hw); + if (adapter->hw.phy.media_type == e1000_media_type_copper) { + if (adapter->hw.mac.type == e1000_i354) + e1000_set_eee_i354(&adapter->hw); + else + e1000_set_eee_i350(&adapter->hw); + } } /* @@ -988,7 +1001,7 @@ igb_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr) { struct adapter *adapter = txr->adapter; struct mbuf *next; - int err = 0, enq; + int err = 0, enq = 0; IGB_TX_LOCK_ASSERT(txr); @@ -996,7 +1009,6 @@ igb_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr) adapter->link_active == 0) return (ENETDOWN); - enq = 0; /* Process the queue */ while ((next = drbr_peek(ifp, txr->br)) != NULL) { @@ -1224,6 +1236,10 @@ igb_ioctl(struct ifnet *ifp, u_long command, caddr_t data) ifp->if_capenable ^= IFCAP_TSO4; reinit = 1; } + if (mask & IFCAP_TSO6) { + ifp->if_capenable ^= IFCAP_TSO6; + reinit = 1; + } if (mask & IFCAP_VLAN_HWTAGGING) { ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; reinit = 1; @@ -1301,7 +1317,7 @@ igb_init_locked(struct adapter *adapter) #endif } - if (ifp->if_capenable & IFCAP_TSO4) + if (ifp->if_capenable & IFCAP_TSO) ifp->if_hwassist |= CSUM_TSO; /* Configure for OS presence */ @@ -1365,8 +1381,12 @@ igb_init_locked(struct adapter *adapter) } /* Set Energy Efficient Ethernet */ - if (adapter->hw.phy.media_type == e1000_media_type_copper) - e1000_set_eee_i350(&adapter->hw); + if (adapter->hw.phy.media_type == e1000_media_type_copper) { + if (adapter->hw.mac.type == e1000_i354) + e1000_set_eee_i354(&adapter->hw); + else + e1000_set_eee_i350(&adapter->hw); + } } static void @@ -1734,6 +1754,9 @@ igb_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) case 1000: ifmr->ifm_active |= IFM_1000_T; break; + case 2500: + ifmr->ifm_active |= IFM_2500_SX; + break; } if (adapter->link_duplex == FULL_DUPLEX) @@ -1810,273 +1833,142 @@ igb_media_change(struct ifnet *ifp) static int igb_xmit(struct tx_ring *txr, struct mbuf **m_headp) { - struct adapter *adapter = txr->adapter; - bus_dma_segment_t segs[IGB_MAX_SCATTER]; - bus_dmamap_t map; - struct igb_tx_buffer *tx_buffer, *tx_buffer_mapped; - union e1000_adv_tx_desc *txd = NULL; - struct mbuf *m_head = *m_headp; - struct ether_vlan_header *eh = NULL; - struct ip *ip = NULL; - struct tcphdr *th = NULL; - u32 hdrlen, cmd_type_len, olinfo_status = 0; - int ehdrlen, poff; - int nsegs, i, first, last = 0; - int error, do_tso, remap = 1; + struct adapter *adapter = txr->adapter; + u32 olinfo_status = 0, cmd_type_len; + int i, j, error, nsegs; + int first; + bool remap = TRUE; + struct mbuf *m_head; + bus_dma_segment_t segs[IGB_MAX_SCATTER]; + bus_dmamap_t map; + struct igb_tx_buf *txbuf; + union e1000_adv_tx_desc *txd = NULL; - /* Set basic descriptor constants */ - cmd_type_len = E1000_ADVTXD_DTYP_DATA; - cmd_type_len |= E1000_ADVTXD_DCMD_IFCS | E1000_ADVTXD_DCMD_DEXT; - if (m_head->m_flags & M_VLANTAG) - cmd_type_len |= E1000_ADVTXD_DCMD_VLE; - -retry: m_head = *m_headp; - do_tso = ((m_head->m_pkthdr.csum_flags & CSUM_TSO) != 0); - hdrlen = ehdrlen = poff = 0; + + /* Basic descriptor defines */ + cmd_type_len = (E1000_ADVTXD_DTYP_DATA | + E1000_ADVTXD_DCMD_IFCS | E1000_ADVTXD_DCMD_DEXT); + + if (m_head->m_flags & M_VLANTAG) + cmd_type_len |= E1000_ADVTXD_DCMD_VLE; + + /* + * Important to capture the first descriptor + * used because it will contain the index of + * the one we tell the hardware to report back + */ + first = txr->next_avail_desc; + txbuf = &txr->tx_buffers[first]; + map = txbuf->map; /* - * Intel recommends entire IP/TCP header length reside in a single - * buffer. If multiple descriptors are used to describe the IP and - * TCP header, each descriptor should describe one or more - * complete headers; descriptors referencing only parts of headers - * are not supported. If all layer headers are not coalesced into - * a single buffer, each buffer should not cross a 4KB boundary, - * or be larger than the maximum read request size. - * Controller also requires modifing IP/TCP header to make TSO work - * so we firstly get a writable mbuf chain then coalesce ethernet/ - * IP/TCP header into a single buffer to meet the requirement of - * controller. This also simplifies IP/TCP/UDP checksum offloading - * which also has similiar restrictions. + * Map the packet for DMA. */ - if (do_tso || m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD) { - if (do_tso || (m_head->m_next != NULL && - m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD)) { - if (M_WRITABLE(*m_headp) == 0) { - m_head = m_dup(*m_headp, M_NOWAIT); - m_freem(*m_headp); - if (m_head == NULL) { - *m_headp = NULL; - return (ENOBUFS); - } - *m_headp = m_head; - } - } - /* - * Assume IPv4, we don't have TSO/checksum offload support - * for IPv6 yet. - */ - ehdrlen = sizeof(struct ether_header); - m_head = m_pullup(m_head, ehdrlen); - if (m_head == NULL) { - *m_headp = NULL; - return (ENOBUFS); - } - eh = mtod(m_head, struct ether_vlan_header *); - if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { - ehdrlen = sizeof(struct ether_vlan_header); - m_head = m_pullup(m_head, ehdrlen); - if (m_head == NULL) { - *m_headp = NULL; - return (ENOBUFS); - } - } - m_head = m_pullup(m_head, ehdrlen + sizeof(struct ip)); - if (m_head == NULL) { - *m_headp = NULL; - return (ENOBUFS); - } - ip = (struct ip *)(mtod(m_head, char *) + ehdrlen); - poff = ehdrlen + (ip->ip_hl << 2); - if (do_tso) { - m_head = m_pullup(m_head, poff + sizeof(struct tcphdr)); - if (m_head == NULL) { - *m_headp = NULL; - return (ENOBUFS); - } - /* - * The pseudo TCP checksum does not include TCP payload - * length so driver should recompute the checksum here - * what hardware expect to see. This is adherence of - * Microsoft's Large Send specification. - */ - th = (struct tcphdr *)(mtod(m_head, char *) + poff); - th->th_sum = in_pseudo(ip->ip_src.s_addr, - ip->ip_dst.s_addr, htons(IPPROTO_TCP)); - /* Keep track of the full header length */ - hdrlen = poff + (th->th_off << 2); - } else if (m_head->m_pkthdr.csum_flags & CSUM_TCP) { - m_head = m_pullup(m_head, poff + sizeof(struct tcphdr)); - if (m_head == NULL) { - *m_headp = NULL; - return (ENOBUFS); - } - th = (struct tcphdr *)(mtod(m_head, char *) + poff); - m_head = m_pullup(m_head, poff + (th->th_off << 2)); - if (m_head == NULL) { - *m_headp = NULL; - return (ENOBUFS); - } - ip = (struct ip *)(mtod(m_head, char *) + ehdrlen); - th = (struct tcphdr *)(mtod(m_head, char *) + poff); - } else if (m_head->m_pkthdr.csum_flags & CSUM_UDP) { - m_head = m_pullup(m_head, poff + sizeof(struct udphdr)); - if (m_head == NULL) { - *m_headp = NULL; - return (ENOBUFS); - } - ip = (struct ip *)(mtod(m_head, char *) + ehdrlen); - } - *m_headp = m_head; - } - - /* - * Map the packet for DMA - * - * Capture the first descriptor index, - * this descriptor will have the index - * of the EOP which is the only one that - * now gets a DONE bit writeback. - */ - first = txr->next_avail_desc; - tx_buffer = &txr->tx_buffers[first]; - tx_buffer_mapped = tx_buffer; - map = tx_buffer->map; - +retry: error = bus_dmamap_load_mbuf_sg(txr->txtag, map, *m_headp, segs, &nsegs, BUS_DMA_NOWAIT); - /* - * There are two types of errors we can (try) to handle: - * - EFBIG means the mbuf chain was too long and bus_dma ran - * out of segments. Defragment the mbuf chain and try again. - * - ENOMEM means bus_dma could not obtain enough bounce buffers - * at this point in time. Defer sending and try again later. - * All other errors, in particular EINVAL, are fatal and prevent the - * mbuf chain from ever going through. Drop it and report error. - */ - if (error == EFBIG && remap) { + if (__predict_false(error)) { struct mbuf *m; - m = m_defrag(*m_headp, M_NOWAIT); - if (m == NULL) { - adapter->mbuf_defrag_failed++; + switch (error) { + case EFBIG: + /* Try it again? - one try */ + if (remap == TRUE) { + remap = FALSE; + m = m_defrag(*m_headp, M_NOWAIT); + if (m == NULL) { + adapter->mbuf_defrag_failed++; + m_freem(*m_headp); + *m_headp = NULL; + return (ENOBUFS); + } + *m_headp = m; + goto retry; + } else + return (error); + case ENOMEM: + txr->no_tx_dma_setup++; + return (error); + default: + txr->no_tx_dma_setup++; m_freem(*m_headp); *m_headp = NULL; - return (ENOBUFS); + return (error); } - *m_headp = m; + } - /* Try it again, but only once */ - remap = 0; - goto retry; - } else if (error == ENOMEM) { - adapter->no_tx_dma_setup++; - return (error); - } else if (error != 0) { - adapter->no_tx_dma_setup++; + /* Make certain there are enough descriptors */ + if (nsegs > txr->tx_avail - 2) { + txr->no_desc_avail++; + bus_dmamap_unload(txr->txtag, map); + return (ENOBUFS); + } + m_head = *m_headp; + + /* + ** Set up the appropriate offload context + ** this will consume the first descriptor + */ + error = igb_tx_ctx_setup(txr, m_head, &cmd_type_len, &olinfo_status); + if (__predict_false(error)) { m_freem(*m_headp); *m_headp = NULL; return (error); } - /* - ** Make sure we don't overrun the ring, - ** we need nsegs descriptors and one for - ** the context descriptor used for the - ** offloads. - */ - if ((nsegs + 1) > (txr->tx_avail - 2)) { - txr->no_desc_avail++; - bus_dmamap_unload(txr->txtag, map); - return (ENOBUFS); - } - m_head = *m_headp; - - /* Do hardware assists: - * Set up the context descriptor, used - * when any hardware offload is done. - * This includes CSUM, VLAN, and TSO. - * It will use the first descriptor. - */ - - if (m_head->m_pkthdr.csum_flags & CSUM_TSO) { - if (igb_tso_setup(txr, m_head, ehdrlen, ip, th)) { - cmd_type_len |= E1000_ADVTXD_DCMD_TSE; - olinfo_status |= E1000_TXD_POPTS_IXSM << 8; - olinfo_status |= E1000_TXD_POPTS_TXSM << 8; - } else - return (ENXIO); - } else if (igb_tx_ctx_setup(txr, m_head)) - olinfo_status |= E1000_TXD_POPTS_TXSM << 8; - - /* Calculate payload length */ - olinfo_status |= ((m_head->m_pkthdr.len - hdrlen) - << E1000_ADVTXD_PAYLEN_SHIFT); - /* 82575 needs the queue index added */ if (adapter->hw.mac.type == e1000_82575) olinfo_status |= txr->me << 4; - /* Set up our transmit descriptors */ i = txr->next_avail_desc; - for (int j = 0; j < nsegs; j++) { - bus_size_t seg_len; - bus_addr_t seg_addr; + for (j = 0; j < nsegs; j++) { + bus_size_t seglen; + bus_addr_t segaddr; - tx_buffer = &txr->tx_buffers[i]; - txd = (union e1000_adv_tx_desc *)&txr->tx_base[i]; - seg_addr = segs[j].ds_addr; - seg_len = segs[j].ds_len; + txbuf = &txr->tx_buffers[i]; + txd = &txr->tx_base[i]; + seglen = segs[j].ds_len; + segaddr = htole64(segs[j].ds_addr); - txd->read.buffer_addr = htole64(seg_addr); - txd->read.cmd_type_len = htole32(cmd_type_len | seg_len); + txd->read.buffer_addr = segaddr; + txd->read.cmd_type_len = htole32(E1000_TXD_CMD_IFCS | + cmd_type_len | seglen); txd->read.olinfo_status = htole32(olinfo_status); - last = i; - if (++i == adapter->num_tx_desc) + + if (++i == txr->num_desc) i = 0; - tx_buffer->m_head = NULL; - tx_buffer->next_eop = -1; } - txr->next_avail_desc = i; + txd->read.cmd_type_len |= + htole32(E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS); txr->tx_avail -= nsegs; - tx_buffer->m_head = m_head; + txr->next_avail_desc = i; + txbuf->m_head = m_head; /* ** Here we swap the map so the last descriptor, ** which gets the completion interrupt has the ** real map, and the first descriptor gets the ** unused map from this descriptor. */ - tx_buffer_mapped->map = tx_buffer->map; - tx_buffer->map = map; - bus_dmamap_sync(txr->txtag, map, BUS_DMASYNC_PREWRITE); + txr->tx_buffers[first].map = txbuf->map; + txbuf->map = map; + bus_dmamap_sync(txr->txtag, map, BUS_DMASYNC_PREWRITE); - /* - * Last Descriptor of Packet - * needs End Of Packet (EOP) - * and Report Status (RS) - */ - txd->read.cmd_type_len |= - htole32(E1000_ADVTXD_DCMD_EOP | E1000_ADVTXD_DCMD_RS); - /* - * Keep track in the first buffer which - * descriptor will be written back - */ - tx_buffer = &txr->tx_buffers[first]; - tx_buffer->next_eop = last; - /* Update the watchdog time early and often */ - txr->watchdog_time = ticks; + /* Set the EOP descriptor that will be marked done */ + txbuf = &txr->tx_buffers[first]; + txbuf->eop = txd; + bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* - * Advance the Transmit Descriptor Tail (TDT), this tells the E1000 - * that this frame is available to transmit. + * Advance the Transmit Descriptor Tail (Tdt), this tells the + * hardware that this frame is available to transmit. */ - bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + ++txr->total_packets; E1000_WRITE_REG(&adapter->hw, E1000_TDT(txr->me), i); - ++txr->tx_packets; return (0); } @@ -2345,6 +2237,17 @@ igb_update_link_status(struct adapter *adapter) if ((ctrl & E1000_CTRL_EXT_LINK_MODE_GMII) && (thstat & E1000_THSTAT_LINK_THROTTLE)) device_printf(dev, "Link: thermal downshift\n"); + /* Delay Link Up for Phy update */ + if (((hw->mac.type == e1000_i210) || + (hw->mac.type == e1000_i211)) && + (hw->phy.id == I210_I_PHY_ID)) + msec_delay(I210_LINK_DELAY); + /* Reset if the media type changed. */ + if (hw->dev_spec._82575.media_changed) { + hw->dev_spec._82575.media_changed = false; + adapter->flags |= IGB_MEDIA_RESET; + igb_reset(adapter); + } /* This can sleep */ if_link_state_change(ifp, LINK_STATE_UP); } else if (!link_check && (adapter->link_active == 1)) { @@ -2477,6 +2380,7 @@ igb_allocate_legacy(struct adapter *adapter) { device_t dev = adapter->dev; struct igb_queue *que = adapter->queues; + struct tx_ring *txr = adapter->tx_rings; int error, rid = 0; /* Turn off all interrupts */ @@ -2496,7 +2400,7 @@ igb_allocate_legacy(struct adapter *adapter) } #ifndef IGB_LEGACY_TX - TASK_INIT(&que->txr->txq_task, 0, igb_deferred_mq_start, que->txr); + TASK_INIT(&txr->txq_task, 0, igb_deferred_mq_start, txr); #endif /* @@ -2633,6 +2537,7 @@ igb_configure_queues(struct adapter *adapter) switch (adapter->hw.mac.type) { case e1000_82580: case e1000_i350: + case e1000_i354: case e1000_i210: case e1000_i211: case e1000_vfadapt: @@ -2818,7 +2723,7 @@ mem: if (adapter->msix_mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, - PCIR_BAR(IGB_MSIX_BAR), adapter->msix_mem); + adapter->memrid, adapter->msix_mem); if (adapter->pci_mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, @@ -2832,8 +2737,8 @@ mem: static int igb_setup_msix(struct adapter *adapter) { - device_t dev = adapter->dev; - int rid, want, queues, msgs, maxqueues; + device_t dev = adapter->dev; + int bar, want, queues, msgs, maxqueues; /* tuneable override */ if (igb_enable_msix == 0) @@ -2843,9 +2748,17 @@ igb_setup_msix(struct adapter *adapter) msgs = pci_msix_count(dev); if (msgs == 0) goto msi; - rid = PCIR_BAR(IGB_MSIX_BAR); + /* + ** Some new devices, as with ixgbe, now may + ** use a different BAR, so we need to keep + ** track of which is used. + */ + adapter->memrid = PCIR_BAR(IGB_MSIX_BAR); + bar = pci_read_config(dev, adapter->memrid, 4); + if (bar == 0) /* use next bar */ + adapter->memrid += 4; adapter->msix_mem = bus_alloc_resource_any(dev, - SYS_RES_MEMORY, &rid, RF_ACTIVE); + SYS_RES_MEMORY, &adapter->memrid, RF_ACTIVE); if (adapter->msix_mem == NULL) { /* May not be enabled */ device_printf(adapter->dev, @@ -2868,6 +2781,7 @@ igb_setup_msix(struct adapter *adapter) case e1000_82576: case e1000_82580: case e1000_i350: + case e1000_i354: maxqueues = 8; break; case e1000_i210: @@ -2883,8 +2797,9 @@ igb_setup_msix(struct adapter *adapter) if (queues > maxqueues) queues = maxqueues; - /* reflect correct sysctl value */ - igb_num_queues = queues; + /* Manual override */ + if (igb_num_queues != 0) + queues = igb_num_queues; /* ** One vector (RX/TX pair) per queue @@ -2927,6 +2842,129 @@ msi: return (0); } +/********************************************************************* + * + * Initialize the DMA Coalescing feature + * + **********************************************************************/ +static void +igb_init_dmac(struct adapter *adapter, u32 pba) +{ + device_t dev = adapter->dev; + struct e1000_hw *hw = &adapter->hw; + u32 dmac, reg = ~E1000_DMACR_DMAC_EN; + u16 hwm; + + if (hw->mac.type == e1000_i211) + return; + + if (hw->mac.type > e1000_82580) { + + if (adapter->dmac == 0) { /* Disabling it */ + E1000_WRITE_REG(hw, E1000_DMACR, reg); + return; + } else + device_printf(dev, "DMA Coalescing enabled\n"); + + /* Set starting threshold */ + E1000_WRITE_REG(hw, E1000_DMCTXTH, 0); + + hwm = 64 * pba - adapter->max_frame_size / 16; + if (hwm < 64 * (pba - 6)) + hwm = 64 * (pba - 6); + reg = E1000_READ_REG(hw, E1000_FCRTC); + reg &= ~E1000_FCRTC_RTH_COAL_MASK; + reg |= ((hwm << E1000_FCRTC_RTH_COAL_SHIFT) + & E1000_FCRTC_RTH_COAL_MASK); + E1000_WRITE_REG(hw, E1000_FCRTC, reg); + + + dmac = pba - adapter->max_frame_size / 512; + if (dmac < pba - 10) + dmac = pba - 10; + reg = E1000_READ_REG(hw, E1000_DMACR); + reg &= ~E1000_DMACR_DMACTHR_MASK; + reg = ((dmac << E1000_DMACR_DMACTHR_SHIFT) + & E1000_DMACR_DMACTHR_MASK); + + /* transition to L0x or L1 if available..*/ + reg |= (E1000_DMACR_DMAC_EN | E1000_DMACR_DMAC_LX_MASK); + + /* Check if status is 2.5Gb backplane connection + * before configuration of watchdog timer, which is + * in msec values in 12.8usec intervals + * watchdog timer= msec values in 32usec intervals + * for non 2.5Gb connection + */ + if (hw->mac.type == e1000_i354) { + int status = E1000_READ_REG(hw, E1000_STATUS); + if ((status & E1000_STATUS_2P5_SKU) && + (!(status & E1000_STATUS_2P5_SKU_OVER))) + reg |= ((adapter->dmac * 5) >> 6); + else + reg |= (adapter->dmac >> 5); + } else { + reg |= (adapter->dmac >> 5); + } + + E1000_WRITE_REG(hw, E1000_DMACR, reg); + +#ifdef I210_OBFF_SUPPORT + /* + * Set the OBFF Rx threshold to DMA Coalescing Rx + * threshold - 2KB and enable the feature in the + * hardware for I210. + */ + if (hw->mac.type == e1000_i210) { + int obff = dmac - 2; + reg = E1000_READ_REG(hw, E1000_DOBFFCTL); + reg &= ~E1000_DOBFFCTL_OBFFTHR_MASK; + reg |= (obff & E1000_DOBFFCTL_OBFFTHR_MASK) + | E1000_DOBFFCTL_EXIT_ACT_MASK; + E1000_WRITE_REG(hw, E1000_DOBFFCTL, reg); + } +#endif + E1000_WRITE_REG(hw, E1000_DMCRTRH, 0); + + /* Set the interval before transition */ + reg = E1000_READ_REG(hw, E1000_DMCTLX); + if (hw->mac.type == e1000_i350) + reg |= IGB_DMCTLX_DCFLUSH_DIS; + /* + ** in 2.5Gb connection, TTLX unit is 0.4 usec + ** which is 0x4*2 = 0xA. But delay is still 4 usec + */ + if (hw->mac.type == e1000_i354) { + int status = E1000_READ_REG(hw, E1000_STATUS); + if ((status & E1000_STATUS_2P5_SKU) && + (!(status & E1000_STATUS_2P5_SKU_OVER))) + reg |= 0xA; + else + reg |= 0x4; + } else { + reg |= 0x4; + } + + E1000_WRITE_REG(hw, E1000_DMCTLX, reg); + + /* free space in tx packet buffer to wake from DMA coal */ + E1000_WRITE_REG(hw, E1000_DMCTXTH, (IGB_TXPBSIZE - + (2 * adapter->max_frame_size)) >> 6); + + /* make low power state decision controlled by DMA coal */ + reg = E1000_READ_REG(hw, E1000_PCIEMISC); + reg &= ~E1000_PCIEMISC_LX_DECISION; + E1000_WRITE_REG(hw, E1000_PCIEMISC, reg); + + } else if (hw->mac.type == e1000_82580) { + u32 reg = E1000_READ_REG(hw, E1000_PCIEMISC); + E1000_WRITE_REG(hw, E1000_PCIEMISC, + reg & ~E1000_PCIEMISC_LX_DECISION); + E1000_WRITE_REG(hw, E1000_DMACR, 0); + } +} + + /********************************************************************* * * Set up an fresh starting state @@ -2963,6 +3001,7 @@ igb_reset(struct adapter *adapter) break; case e1000_82580: case e1000_i350: + case e1000_i354: case e1000_vfadapt_i350: pba = E1000_READ_REG(hw, E1000_RXPBS); pba = e1000_rxpbs_adjust_82580(pba); @@ -3033,70 +3072,19 @@ igb_reset(struct adapter *adapter) e1000_reset_hw(hw); E1000_WRITE_REG(hw, E1000_WUC, 0); + /* Reset for AutoMediaDetect */ + if (adapter->flags & IGB_MEDIA_RESET) { + e1000_setup_init_funcs(hw, TRUE); + e1000_get_bus_info(hw); + adapter->flags &= ~IGB_MEDIA_RESET; + } + if (e1000_init_hw(hw) < 0) device_printf(dev, "Hardware Initialization Failed\n"); /* Setup DMA Coalescing */ - if ((hw->mac.type > e1000_82580) && - (hw->mac.type != e1000_i211)) { - u32 dmac; - u32 reg = ~E1000_DMACR_DMAC_EN; + igb_init_dmac(adapter, pba); - if (adapter->dmac == 0) { /* Disabling it */ - E1000_WRITE_REG(hw, E1000_DMACR, reg); - goto reset_out; - } - - /* Set starting thresholds */ - E1000_WRITE_REG(hw, E1000_DMCTXTH, 0); - E1000_WRITE_REG(hw, E1000_DMCRTRH, 0); - - hwm = 64 * pba - adapter->max_frame_size / 16; - if (hwm < 64 * (pba - 6)) - hwm = 64 * (pba - 6); - reg = E1000_READ_REG(hw, E1000_FCRTC); - reg &= ~E1000_FCRTC_RTH_COAL_MASK; - reg |= ((hwm << E1000_FCRTC_RTH_COAL_SHIFT) - & E1000_FCRTC_RTH_COAL_MASK); - E1000_WRITE_REG(hw, E1000_FCRTC, reg); - - - dmac = pba - adapter->max_frame_size / 512; - if (dmac < pba - 10) - dmac = pba - 10; - reg = E1000_READ_REG(hw, E1000_DMACR); - reg &= ~E1000_DMACR_DMACTHR_MASK; - reg = ((dmac << E1000_DMACR_DMACTHR_SHIFT) - & E1000_DMACR_DMACTHR_MASK); - /* transition to L0x or L1 if available..*/ - reg |= (E1000_DMACR_DMAC_EN | E1000_DMACR_DMAC_LX_MASK); - /* timer = value in adapter->dmac in 32usec intervals */ - reg |= (adapter->dmac >> 5); - E1000_WRITE_REG(hw, E1000_DMACR, reg); - - /* Set the interval before transition */ - reg = E1000_READ_REG(hw, E1000_DMCTLX); - reg |= 0x80000004; - E1000_WRITE_REG(hw, E1000_DMCTLX, reg); - - /* free space in tx packet buffer to wake from DMA coal */ - E1000_WRITE_REG(hw, E1000_DMCTXTH, - (20480 - (2 * adapter->max_frame_size)) >> 6); - - /* make low power state decision controlled by DMA coal */ - reg = E1000_READ_REG(hw, E1000_PCIEMISC); - reg &= ~E1000_PCIEMISC_LX_DECISION; - E1000_WRITE_REG(hw, E1000_PCIEMISC, reg); - device_printf(dev, "DMA Coalescing enabled\n"); - - } else if (hw->mac.type == e1000_82580) { - u32 reg = E1000_READ_REG(hw, E1000_PCIEMISC); - E1000_WRITE_REG(hw, E1000_DMACR, 0); - E1000_WRITE_REG(hw, E1000_PCIEMISC, - reg & ~E1000_PCIEMISC_LX_DECISION); - } - -reset_out: E1000_WRITE_REG(&adapter->hw, E1000_VET, ETHERTYPE_VLAN); e1000_get_phy_info(hw); e1000_check_for_link(hw); @@ -3140,7 +3128,7 @@ igb_setup_interface(device_t dev, struct adapter *adapter) ifp->if_capabilities = ifp->if_capenable = 0; ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM; - ifp->if_capabilities |= IFCAP_TSO4; + ifp->if_capabilities |= IFCAP_TSO; ifp->if_capabilities |= IFCAP_JUMBO_MTU; ifp->if_capenable = ifp->if_capabilities; @@ -3346,6 +3334,7 @@ igb_allocate_queues(struct adapter *adapter) txr = &adapter->tx_rings[i]; txr->adapter = adapter; txr->me = i; + txr->num_desc = adapter->num_tx_desc; /* Initialize the TX lock */ snprintf(txr->mtx_name, sizeof(txr->mtx_name), "%s:tx(%d)", @@ -3359,7 +3348,7 @@ igb_allocate_queues(struct adapter *adapter) error = ENOMEM; goto err_tx_desc; } - txr->tx_base = (struct e1000_tx_desc *)txr->txdma.dma_vaddr; + txr->tx_base = (union e1000_adv_tx_desc *)txr->txdma.dma_vaddr; bzero((void *)txr->tx_base, tsize); /* Now allocate transmit buffers for the ring */ @@ -3452,7 +3441,7 @@ igb_allocate_transmit_buffers(struct tx_ring *txr) { struct adapter *adapter = txr->adapter; device_t dev = adapter->dev; - struct igb_tx_buffer *txbuf; + struct igb_tx_buf *txbuf; int error, i; /* @@ -3475,7 +3464,7 @@ igb_allocate_transmit_buffers(struct tx_ring *txr) } if (!(txr->tx_buffers = - (struct igb_tx_buffer *) malloc(sizeof(struct igb_tx_buffer) * + (struct igb_tx_buf *) malloc(sizeof(struct igb_tx_buf) * adapter->num_tx_desc, M_DEVBUF, M_NOWAIT | M_ZERO))) { device_printf(dev, "Unable to allocate tx_buffer memory\n"); error = ENOMEM; @@ -3508,7 +3497,7 @@ static void igb_setup_transmit_ring(struct tx_ring *txr) { struct adapter *adapter = txr->adapter; - struct igb_tx_buffer *txbuf; + struct igb_tx_buf *txbuf; int i; #ifdef DEV_NETMAP struct netmap_adapter *na = NA(adapter->ifp); @@ -3544,7 +3533,7 @@ igb_setup_transmit_ring(struct tx_ring *txr) } #endif /* DEV_NETMAP */ /* clear the watch index */ - txbuf->next_eop = -1; + txbuf->eop = NULL; } /* Set number of descriptors available */ @@ -3658,7 +3647,7 @@ static void igb_free_transmit_buffers(struct tx_ring *txr) { struct adapter *adapter = txr->adapter; - struct igb_tx_buffer *tx_buffer; + struct igb_tx_buf *tx_buffer; int i; INIT_DEBUGOUT("free_transmit_ring: begin"); @@ -3705,44 +3694,100 @@ igb_free_transmit_buffers(struct tx_ring *txr) /********************************************************************** * - * Setup work for hardware segmentation offload (TSO) + * Setup work for hardware segmentation offload (TSO) on + * adapters using advanced tx descriptors * **********************************************************************/ -static bool -igb_tso_setup(struct tx_ring *txr, struct mbuf *mp, int ehdrlen, - struct ip *ip, struct tcphdr *th) +static int +igb_tso_setup(struct tx_ring *txr, struct mbuf *mp, + u32 *cmd_type_len, u32 *olinfo_status) { struct adapter *adapter = txr->adapter; struct e1000_adv_tx_context_desc *TXD; - struct igb_tx_buffer *tx_buffer; u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0; - u32 mss_l4len_idx = 0; - u16 vtag = 0; - int ctxd, ip_hlen, tcp_hlen; + u32 mss_l4len_idx = 0, paylen; + u16 vtag = 0, eh_type; + int ctxd, ehdrlen, ip_hlen, tcp_hlen; + struct ether_vlan_header *eh; +#ifdef INET6 + struct ip6_hdr *ip6; +#endif +#ifdef INET + struct ip *ip; +#endif + struct tcphdr *th; + + + /* + * Determine where frame payload starts. + * Jump over vlan headers if already present + */ + eh = mtod(mp, struct ether_vlan_header *); + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + eh_type = eh->evl_proto; + } else { + ehdrlen = ETHER_HDR_LEN; + eh_type = eh->evl_encap_proto; + } + + switch (ntohs(eh_type)) { +#ifdef INET6 + case ETHERTYPE_IPV6: + ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); + /* XXX-BZ For now we do not pretend to support ext. hdrs. */ + if (ip6->ip6_nxt != IPPROTO_TCP) + return (ENXIO); + ip_hlen = sizeof(struct ip6_hdr); + ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); + th = (struct tcphdr *)((caddr_t)ip6 + ip_hlen); + th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0); + type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV6; + break; +#endif +#ifdef INET + case ETHERTYPE_IP: + ip = (struct ip *)(mp->m_data + ehdrlen); + if (ip->ip_p != IPPROTO_TCP) + return (ENXIO); + ip->ip_sum = 0; + ip_hlen = ip->ip_hl << 2; + th = (struct tcphdr *)((caddr_t)ip + ip_hlen); + th->th_sum = in_pseudo(ip->ip_src.s_addr, + ip->ip_dst.s_addr, htons(IPPROTO_TCP)); + type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV4; + /* Tell transmit desc to also do IPv4 checksum. */ + *olinfo_status |= E1000_TXD_POPTS_IXSM << 8; + break; +#endif + default: + panic("%s: CSUM_TSO but no supported IP version (0x%04x)", + __func__, ntohs(eh_type)); + break; + } ctxd = txr->next_avail_desc; - tx_buffer = &txr->tx_buffers[ctxd]; TXD = (struct e1000_adv_tx_context_desc *) &txr->tx_base[ctxd]; - ip->ip_sum = 0; - ip_hlen = ip->ip_hl << 2; tcp_hlen = th->th_off << 2; + /* This is used in the transmit desc in encap */ + paylen = mp->m_pkthdr.len - ehdrlen - ip_hlen - tcp_hlen; + /* VLAN MACLEN IPLEN */ if (mp->m_flags & M_VLANTAG) { vtag = htole16(mp->m_pkthdr.ether_vtag); - vlan_macip_lens |= (vtag << E1000_ADVTXD_VLAN_SHIFT); + vlan_macip_lens |= (vtag << E1000_ADVTXD_VLAN_SHIFT); } - vlan_macip_lens |= (ehdrlen << E1000_ADVTXD_MACLEN_SHIFT); + vlan_macip_lens |= ehdrlen << E1000_ADVTXD_MACLEN_SHIFT; vlan_macip_lens |= ip_hlen; - TXD->vlan_macip_lens |= htole32(vlan_macip_lens); + TXD->vlan_macip_lens = htole32(vlan_macip_lens); /* ADV DTYPE TUCMD */ type_tucmd_mlhl |= E1000_ADVTXD_DCMD_DEXT | E1000_ADVTXD_DTYP_CTXT; type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_TCP; - type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV4; - TXD->type_tucmd_mlhl |= htole32(type_tucmd_mlhl); + TXD->type_tucmd_mlhl = htole32(type_tucmd_mlhl); /* MSS L4LEN IDX */ mss_l4len_idx |= (mp->m_pkthdr.tso_segsz << E1000_ADVTXD_MSS_SHIFT); @@ -3753,57 +3798,65 @@ igb_tso_setup(struct tx_ring *txr, struct mbuf *mp, int ehdrlen, TXD->mss_l4len_idx = htole32(mss_l4len_idx); TXD->seqnum_seed = htole32(0); - tx_buffer->m_head = NULL; - tx_buffer->next_eop = -1; - if (++ctxd == adapter->num_tx_desc) + if (++ctxd == txr->num_desc) ctxd = 0; txr->tx_avail--; txr->next_avail_desc = ctxd; - return TRUE; + *cmd_type_len |= E1000_ADVTXD_DCMD_TSE; + *olinfo_status |= E1000_TXD_POPTS_TXSM << 8; + *olinfo_status |= paylen << E1000_ADVTXD_PAYLEN_SHIFT; + ++txr->tso_tx; + return (0); } - /********************************************************************* * - * Context Descriptor setup for VLAN or CSUM + * Advanced Context Descriptor setup for VLAN, CSUM or TSO * **********************************************************************/ -static bool -igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp) +static int +igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp, + u32 *cmd_type_len, u32 *olinfo_status) { - struct adapter *adapter = txr->adapter; struct e1000_adv_tx_context_desc *TXD; - struct igb_tx_buffer *tx_buffer; - u32 vlan_macip_lens, type_tucmd_mlhl, mss_l4len_idx; + struct adapter *adapter = txr->adapter; struct ether_vlan_header *eh; - struct ip *ip = NULL; + struct ip *ip; struct ip6_hdr *ip6; - int ehdrlen, ctxd, ip_hlen = 0; - u16 etype, vtag = 0; + u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0, mss_l4len_idx = 0; + int ehdrlen, ip_hlen = 0; + u16 etype; u8 ipproto = 0; - bool offload = TRUE; + int offload = TRUE; + int ctxd = txr->next_avail_desc; + u16 vtag = 0; + + /* First check if TSO is to be used */ + if (mp->m_pkthdr.csum_flags & CSUM_TSO) + return (igb_tso_setup(txr, mp, cmd_type_len, olinfo_status)); if ((mp->m_pkthdr.csum_flags & CSUM_OFFLOAD) == 0) offload = FALSE; - vlan_macip_lens = type_tucmd_mlhl = mss_l4len_idx = 0; - ctxd = txr->next_avail_desc; - tx_buffer = &txr->tx_buffers[ctxd]; + /* Indicate the whole packet as payload when not doing TSO */ + *olinfo_status |= mp->m_pkthdr.len << E1000_ADVTXD_PAYLEN_SHIFT; + + /* Now ready a context descriptor */ TXD = (struct e1000_adv_tx_context_desc *) &txr->tx_base[ctxd]; /* ** In advanced descriptors the vlan tag must - ** be placed into the context descriptor, thus - ** we need to be here just for that setup. + ** be placed into the context descriptor. Hence + ** we need to make one even if not doing offloads. */ if (mp->m_flags & M_VLANTAG) { vtag = htole16(mp->m_pkthdr.ether_vtag); vlan_macip_lens |= (vtag << E1000_ADVTXD_VLAN_SHIFT); - } else if (offload == FALSE) - return FALSE; + } else if (offload == FALSE) /* ... no offload to do */ + return (0); /* * Determine where frame payload starts. @@ -3826,16 +3879,13 @@ igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp) case ETHERTYPE_IP: ip = (struct ip *)(mp->m_data + ehdrlen); ip_hlen = ip->ip_hl << 2; - if (mp->m_len < ehdrlen + ip_hlen) { - offload = FALSE; - break; - } ipproto = ip->ip_p; type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV4; break; case ETHERTYPE_IPV6: ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); ip_hlen = sizeof(struct ip6_hdr); + /* XXX-BZ this will go badly in case of ext hdrs. */ ipproto = ip6->ip6_nxt; type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV6; break; @@ -3856,6 +3906,7 @@ igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp) if (mp->m_pkthdr.csum_flags & CSUM_UDP) type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_UDP; break; + #if __FreeBSD_version >= 800000 case IPPROTO_SCTP: if (mp->m_pkthdr.csum_flags & CSUM_SCTP) @@ -3867,29 +3918,28 @@ igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp) break; } + if (offload) /* For the TX descriptor setup */ + *olinfo_status |= E1000_TXD_POPTS_TXSM << 8; + /* 82575 needs the queue index added */ if (adapter->hw.mac.type == e1000_82575) mss_l4len_idx = txr->me << 4; /* Now copy bits into descriptor */ - TXD->vlan_macip_lens |= htole32(vlan_macip_lens); - TXD->type_tucmd_mlhl |= htole32(type_tucmd_mlhl); + TXD->vlan_macip_lens = htole32(vlan_macip_lens); + TXD->type_tucmd_mlhl = htole32(type_tucmd_mlhl); TXD->seqnum_seed = htole32(0); TXD->mss_l4len_idx = htole32(mss_l4len_idx); - tx_buffer->m_head = NULL; - tx_buffer->next_eop = -1; - /* We've consumed the first desc, adjust counters */ - if (++ctxd == adapter->num_tx_desc) + if (++ctxd == txr->num_desc) ctxd = 0; txr->next_avail_desc = ctxd; --txr->tx_avail; - return (offload); + return (0); } - /********************************************************************** * * Examine each tx_buffer in the used queue. If the hardware is done @@ -3901,90 +3951,106 @@ igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp) static bool igb_txeof(struct tx_ring *txr) { - struct adapter *adapter = txr->adapter; - int first, last, done, processed; - struct igb_tx_buffer *tx_buffer; - struct e1000_tx_desc *tx_desc, *eop_desc; - struct ifnet *ifp = adapter->ifp; + struct adapter *adapter = txr->adapter; + struct ifnet *ifp = adapter->ifp; + u32 work, processed = 0; + u16 limit = txr->process_limit; + struct igb_tx_buf *buf; + union e1000_adv_tx_desc *txd; - IGB_TX_LOCK_ASSERT(txr); + mtx_assert(&txr->tx_mtx, MA_OWNED); #ifdef DEV_NETMAP if (netmap_tx_irq(ifp, txr->me | (NETMAP_LOCKED_ENTER|NETMAP_LOCKED_EXIT))) return (FALSE); #endif /* DEV_NETMAP */ - if (txr->tx_avail == adapter->num_tx_desc) { + + if (txr->tx_avail == txr->num_desc) { txr->queue_status = IGB_QUEUE_IDLE; - return FALSE; + return FALSE; } - processed = 0; - first = txr->next_to_clean; - tx_desc = &txr->tx_base[first]; - tx_buffer = &txr->tx_buffers[first]; - last = tx_buffer->next_eop; - eop_desc = &txr->tx_base[last]; - - /* - * What this does is get the index of the - * first descriptor AFTER the EOP of the - * first packet, that way we can do the - * simple comparison on the inner while loop. - */ - if (++last == adapter->num_tx_desc) - last = 0; - done = last; - + /* Get work starting point */ + work = txr->next_to_clean; + buf = &txr->tx_buffers[work]; + txd = &txr->tx_base[work]; + work -= txr->num_desc; /* The distance to ring end */ bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + do { + union e1000_adv_tx_desc *eop = buf->eop; + if (eop == NULL) /* No work */ + break; - while (eop_desc->upper.fields.status & E1000_TXD_STAT_DD) { - /* We clean the range of the packet */ - while (first != done) { - tx_desc->upper.data = 0; - tx_desc->lower.data = 0; - tx_desc->buffer_addr = 0; - ++txr->tx_avail; - ++processed; + if ((eop->wb.status & E1000_TXD_STAT_DD) == 0) + break; /* I/O not complete */ - if (tx_buffer->m_head) { + if (buf->m_head) { + txr->bytes += + buf->m_head->m_pkthdr.len; + bus_dmamap_sync(txr->txtag, + buf->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txr->txtag, + buf->map); + m_freem(buf->m_head); + buf->m_head = NULL; + buf->map = NULL; + } + buf->eop = NULL; + ++txr->tx_avail; + + /* We clean the range if multi segment */ + while (txd != eop) { + ++txd; + ++buf; + ++work; + /* wrap the ring? */ + if (__predict_false(!work)) { + work -= txr->num_desc; + buf = txr->tx_buffers; + txd = txr->tx_base; + } + if (buf->m_head) { txr->bytes += - tx_buffer->m_head->m_pkthdr.len; + buf->m_head->m_pkthdr.len; bus_dmamap_sync(txr->txtag, - tx_buffer->map, + buf->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(txr->txtag, - tx_buffer->map); + buf->map); + m_freem(buf->m_head); + buf->m_head = NULL; + buf->map = NULL; + } + ++txr->tx_avail; + buf->eop = NULL; - m_freem(tx_buffer->m_head); - tx_buffer->m_head = NULL; - } - tx_buffer->next_eop = -1; - txr->watchdog_time = ticks; - - if (++first == adapter->num_tx_desc) - first = 0; - - tx_buffer = &txr->tx_buffers[first]; - tx_desc = &txr->tx_base[first]; } ++txr->packets; + ++processed; ++ifp->if_opackets; - /* See if we can continue to the next packet */ - last = tx_buffer->next_eop; - if (last != -1) { - eop_desc = &txr->tx_base[last]; - /* Get new done point */ - if (++last == adapter->num_tx_desc) last = 0; - done = last; - } else - break; - } - bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + txr->watchdog_time = ticks; - txr->next_to_clean = first; + /* Try the next packet */ + ++txd; + ++buf; + ++work; + /* reset with a wrap */ + if (__predict_false(!work)) { + work -= txr->num_desc; + buf = txr->tx_buffers; + txd = txr->tx_base; + } + prefetch(txd); + } while (__predict_true(--limit)); + + bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + work += txr->num_desc; + txr->next_to_clean = work; /* ** Watchdog calculation, we know there's @@ -3994,18 +4060,14 @@ igb_txeof(struct tx_ring *txr) */ if ((!processed) && ((ticks - txr->watchdog_time) > IGB_WATCHDOG)) txr->queue_status |= IGB_QUEUE_HUNG; - /* - * If we have a minimum free, - * clear depleted state bit - */ - if (txr->tx_avail >= IGB_QUEUE_THRESHOLD) - txr->queue_status &= ~IGB_QUEUE_DEPLETED; - /* All clean, turn off the watchdog */ - if (txr->tx_avail == adapter->num_tx_desc) { + if (txr->tx_avail >= IGB_QUEUE_THRESHOLD) + txr->queue_status &= ~IGB_QUEUE_DEPLETED; + + if (txr->tx_avail == txr->num_desc) { txr->queue_status = IGB_QUEUE_IDLE; return (FALSE); - } + } return (TRUE); } @@ -4789,7 +4851,8 @@ igb_rxeof(struct igb_queue *que, int count, int *done) rxbuf = &rxr->rx_buffers[i]; plen = le16toh(cur->wb.upper.length); ptype = le32toh(cur->wb.lower.lo_dword.data) & IGB_PKTTYPE_MASK; - if ((adapter->hw.mac.type == e1000_i350) && + if (((adapter->hw.mac.type == e1000_i350) || + (adapter->hw.mac.type == e1000_i354)) && (staterr & E1000_RXDEXT_STATERR_LB)) vtag = be16toh(cur->wb.upper.vlan); else @@ -5590,8 +5653,8 @@ igb_add_hw_stats(struct adapter *adapter) SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "no_desc_avail", CTLFLAG_RD, &txr->no_desc_avail, "Queue No Descriptor Available"); - SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tx_packets", - CTLFLAG_RD, &txr->tx_packets, + SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_packets", + CTLFLAG_RD, &txr->total_packets, "Queue Packets Transmitted"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_head", @@ -6032,7 +6095,7 @@ igb_sysctl_dmac(SYSCTL_HANDLER_ARGS) default: /* Do nothing, illegal value */ adapter->dmac = 0; - return (error); + return (EINVAL); } /* Reinit the interface */ igb_init(adapter); diff --git a/sys/dev/e1000/if_igb.h b/sys/dev/e1000/if_igb.h index 6f3a3a54efb..0c4474126f7 100644 --- a/sys/dev/e1000/if_igb.h +++ b/sys/dev/e1000/if_igb.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2013, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -162,6 +162,9 @@ /* PHY master/slave setting */ #define IGB_MASTER_SLAVE e1000_ms_hw_default +/* Support AutoMediaDetect for Marvell M88 PHY in i354 */ +#define IGB_MEDIA_RESET (1 << 0) + /* * Micellaneous constants */ @@ -173,11 +176,13 @@ #define IGB_SMARTSPEED_MAX 15 #define IGB_MAX_LOOP 10 -#define IGB_RX_PTHRESH (hw->mac.type <= e1000_82576 ? 16 : 8) +#define IGB_RX_PTHRESH ((hw->mac.type == e1000_i354) ? 12 : \ + ((hw->mac.type <= e1000_82576) ? 16 : 8)) #define IGB_RX_HTHRESH 8 -#define IGB_RX_WTHRESH 1 +#define IGB_RX_WTHRESH ((hw->mac.type == e1000_82576 && \ + adapter->msix_mem) ? 1 : 4) -#define IGB_TX_PTHRESH 8 +#define IGB_TX_PTHRESH ((hw->mac.type == e1000_i354) ? 20 : 8) #define IGB_TX_HTHRESH 1 #define IGB_TX_WTHRESH ((hw->mac.type != e1000_82575 && \ adapter->msix_mem) ? 1 : 16) @@ -190,11 +195,6 @@ #define IGB_EEPROM_APME 0x400; /* Queue minimum free for use */ #define IGB_QUEUE_THRESHOLD (adapter->num_tx_desc / 8) -/* Queue bit defines */ -#define IGB_QUEUE_IDLE 1 -#define IGB_QUEUE_WORKING 2 -#define IGB_QUEUE_HUNG 4 -#define IGB_QUEUE_DEPLETED 8 /* * TDBA/RDBA should be aligned on 16 byte boundary. But TDLEN/RDLEN should be @@ -228,8 +228,10 @@ #define IGB_BR_SIZE 4096 /* ring buf size */ #define IGB_TSO_SIZE (65535 + sizeof(struct ether_vlan_header)) #define IGB_TSO_SEG_SIZE 4096 /* Max dma segment size */ +#define IGB_TXPBSIZE 20408 #define IGB_HDR_BUF 128 #define IGB_PKTTYPE_MASK 0x0000FFF0 +#define IGB_DMCTLX_DCFLUSH_DIS 0x80000000 /* Disable DMA Coalesce Flush */ #define ETH_ZLEN 60 #define ETH_ADDR_LEN 6 @@ -245,6 +247,7 @@ #define IGB_DEFAULT_ITR ((1000000/IGB_INTS_PER_SEC) << 2) #define IGB_LINK_ITR 2000 +#define I210_LINK_DELAY 1000 /* Precision Time Sync (IEEE 1588) defines */ #define ETHERTYPE_IEEE1588 0x88F7 @@ -284,34 +287,42 @@ struct igb_queue { }; /* - * Transmit ring: one per queue + * The transmit ring, one per queue */ struct tx_ring { - struct adapter *adapter; - u32 me; + struct adapter *adapter; struct mtx tx_mtx; - char mtx_name[16]; + u32 me; + int watchdog_time; + union e1000_adv_tx_desc *tx_base; + struct igb_tx_buf *tx_buffers; struct igb_dma_alloc txdma; - struct e1000_tx_desc *tx_base; - u32 next_avail_desc; - u32 next_to_clean; volatile u16 tx_avail; - struct igb_tx_buffer *tx_buffers; + u16 next_avail_desc; + u16 next_to_clean; + u16 process_limit; + u16 num_desc; + enum { + IGB_QUEUE_IDLE = 1, + IGB_QUEUE_WORKING = 2, + IGB_QUEUE_HUNG = 4, + IGB_QUEUE_DEPLETED = 8, + } queue_status; + u32 txd_cmd; + bus_dma_tag_t txtag; + char mtx_name[16]; #ifndef IGB_LEGACY_TX struct buf_ring *br; struct task txq_task; #endif - bus_dma_tag_t txtag; - - u32 bytes; + u32 bytes; /* used for AIM */ u32 packets; - - int queue_status; - int watchdog_time; - int tdt; - int tdh; + /* Soft Stats */ + unsigned long tso_tx; + unsigned long no_tx_map_avail; + unsigned long no_tx_dma_setup; u64 no_desc_avail; - u64 tx_packets; + u64 total_packets; }; /* @@ -353,43 +364,38 @@ struct rx_ring { }; struct adapter { - struct ifnet *ifp; - struct e1000_hw hw; + struct ifnet *ifp; + struct e1000_hw hw; - struct e1000_osdep osdep; - struct device *dev; - struct cdev *led_dev; + struct e1000_osdep osdep; + struct device *dev; + struct cdev *led_dev; - struct resource *pci_mem; - struct resource *msix_mem; - struct resource *res; - void *tag; - u32 que_mask; + struct resource *pci_mem; + struct resource *msix_mem; + int memrid; - int linkvec; - int link_mask; - struct task link_task; - int link_irq; + /* + * Interrupt resources: this set is + * either used for legacy, or for Link + * when doing MSIX + */ + void *tag; + struct resource *res; - struct ifmedia media; - struct callout timer; - int msix; /* total vectors allocated */ - int if_flags; - int max_frame_size; - int min_frame_size; - int pause_frames; - struct mtx core_mtx; - int igb_insert_vlan_header; - u16 num_queues; - u16 vf_ifp; /* a VF interface */ + struct ifmedia media; + struct callout timer; + int msix; + int if_flags; + int pause_frames; - eventhandler_tag vlan_attach; - eventhandler_tag vlan_detach; - u32 num_vlans; + struct mtx core_mtx; - /* Management and WOL features */ - int wol; - int has_manage; + eventhandler_tag vlan_attach; + eventhandler_tag vlan_detach; + + u16 num_vlans; + u16 num_queues; /* ** Shadow VFTA table, this is needed because @@ -397,66 +403,86 @@ struct adapter { ** a soft reset and the driver needs to be able ** to repopulate it. */ - u32 shadow_vfta[IGB_VFTA_SIZE]; + u32 shadow_vfta[IGB_VFTA_SIZE]; /* Info about the interface */ - u16 link_active; - u16 fc; - u16 link_speed; - u16 link_duplex; - u32 smartspeed; - u32 dmac; - int enable_aim; + u32 optics; + u32 fc; /* local flow ctrl setting */ + int advertise; /* link speeds */ + bool link_active; + u16 max_frame_size; + u16 num_segs; + u16 link_speed; + bool link_up; + u32 linkvec; + u16 link_duplex; + u32 dmac; + int link_mask; - /* Interface queues */ + /* Flags */ + u32 flags; + + /* Mbuf cluster size */ + u32 rx_mbuf_sz; + + /* Support for pluggable optics */ + bool sfp_probe; + struct task link_task; /* Link tasklet */ + struct task mod_task; /* SFP tasklet */ + struct task msf_task; /* Multispeed Fiber */ + struct taskqueue *tq; + + /* + ** Queues: + ** This is the irq holder, it has + ** and RX/TX pair or rings associated + ** with it. + */ struct igb_queue *queues; /* - * Transmit rings + * Transmit rings: + * Allocated at run time, an array of rings. */ struct tx_ring *tx_rings; - u16 num_tx_desc; + u32 num_tx_desc; - /* Multicast array pointer */ - u8 *mta; - - /* - * Receive rings + /* + * Receive rings: + * Allocated at run time, an array of rings. */ struct rx_ring *rx_rings; - bool rx_hdr_split; - u16 num_rx_desc; - int rx_process_limit; - u32 rx_mbuf_sz; - u32 rx_mask; + u64 que_mask; + u32 num_rx_desc; + + /* Multicast array memory */ + u8 *mta; /* Misc stats maintained by the driver */ - unsigned long dropped_pkts; - unsigned long mbuf_defrag_failed; - unsigned long mbuf_header_failed; - unsigned long mbuf_packet_failed; - unsigned long no_tx_map_avail; - unsigned long no_tx_dma_setup; - unsigned long watchdog_events; - unsigned long rx_overruns; - unsigned long device_control; - unsigned long rx_control; - unsigned long int_mask; - unsigned long eint_mask; - unsigned long packet_buf_alloc_rx; - unsigned long packet_buf_alloc_tx; + unsigned long dropped_pkts; + unsigned long mbuf_defrag_failed; + unsigned long mbuf_header_failed; + unsigned long mbuf_packet_failed; + unsigned long no_tx_dma_setup; + unsigned long watchdog_events; + unsigned long link_irq; + unsigned long rx_overruns; + unsigned long device_control; + unsigned long rx_control; + unsigned long int_mask; + unsigned long eint_mask; + unsigned long packet_buf_alloc_rx; + unsigned long packet_buf_alloc_tx; + /* Used in pf and vf */ + void *stats; - boolean_t in_detach; + int enable_aim; + int has_manage; + int wol; + int rx_process_limit; + u16 vf_ifp; /* a VF interface */ + bool in_detach; /* Used only in igb_ioctl */ -#ifdef IGB_IEEE1588 - /* IEEE 1588 precision time support */ - struct cyclecounter cycles; - struct nettimer clock; - struct nettime_compare compare; - struct hwtstamp_ctrl hwtstamp; -#endif - - void *stats; }; /* ****************************************************************************** @@ -474,11 +500,10 @@ typedef struct _igb_vendor_info_t { unsigned int index; } igb_vendor_info_t; - -struct igb_tx_buffer { - int next_eop; /* Index of the desc to watch */ - struct mbuf *m_head; - bus_dmamap_t map; /* bus_dma map for packet */ +struct igb_tx_buf { + union e1000_adv_tx_desc *eop; + struct mbuf *m_head; + bus_dmamap_t map; }; struct igb_rx_buf { diff --git a/sys/dev/netmap/if_igb_netmap.h b/sys/dev/netmap/if_igb_netmap.h index a94ca3b1883..d4e5dfe0d1c 100644 --- a/sys/dev/netmap/if_igb_netmap.h +++ b/sys/dev/netmap/if_igb_netmap.h @@ -153,7 +153,7 @@ igb_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) /* curr is the current slot in the nic ring */ union e1000_adv_tx_desc *curr = (union e1000_adv_tx_desc *)&txr->tx_base[l]; - struct igb_tx_buffer *txbuf = &txr->tx_buffers[l]; + struct igb_tx_buf *txbuf = &txr->tx_buffers[l]; int flags = ((slot->flags & NS_REPORT) || j == 0 || j == report_frequency) ? E1000_ADVTXD_DCMD_RS : 0; From c48f182bb1b94f86ab8284606decea994f8c7b75 Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 9 Oct 2013 17:34:45 +0000 Subject: [PATCH 18/18] Remove no longer useful debugging output and a stale comment. Approved by: re (gjb) Sponsored by: FreeBSD Foundation --- sys/cam/ctl/ctl_frontend_iscsi.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/sys/cam/ctl/ctl_frontend_iscsi.c b/sys/cam/ctl/ctl_frontend_iscsi.c index 52d30af81f3..597c0fbe7e1 100644 --- a/sys/cam/ctl/ctl_frontend_iscsi.c +++ b/sys/cam/ctl/ctl_frontend_iscsi.c @@ -229,8 +229,6 @@ cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request) /* * The target MUST silently ignore any non-immediate command outside * of this range. - * - * XXX: ... or non-immediate duplicates within the range. */ if (cmdsn < cs->cs_cmdsn || cmdsn > cs->cs_cmdsn + maxcmdsn_delta) { CFISCSI_SESSION_UNLOCK(cs); @@ -720,10 +718,6 @@ cfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *c ctl_sglist->len = io->scsiio.kern_data_len; ctl_sg_count = 1; } -#if 0 - if (ctl_sg_count > 1) - CFISCSI_SESSION_DEBUG(cs, "ctl_sg_count = %d", ctl_sg_count); -#endif if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_DATA_OUT) @@ -2340,11 +2334,6 @@ cfiscsi_datamove_in(union ctl_io *io) */ PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len; -#if 0 - if (ctl_sg_count > 1) - CFISCSI_SESSION_DEBUG(cs, "ctl_sg_count = %d", ctl_sg_count); -#endif - /* * This is the offset within the current SCSI command; * i.e. for the first call of datamove(), it will be 0, @@ -2352,8 +2341,6 @@ cfiscsi_datamove_in(union ctl_io *io) * of previous ones. */ off = htonl(io->scsiio.kern_rel_offset); - if (off > 1) - CFISCSI_SESSION_DEBUG(cs, "off = %zd", off); i = 0; addr = NULL; @@ -2445,10 +2432,12 @@ cfiscsi_datamove_in(union ctl_io *io) if (response != NULL) { if (off == io->scsiio.kern_total_len) { bhsdi->bhsdi_flags |= BHSDI_FLAGS_F; +#if 0 } else { CFISCSI_SESSION_DEBUG(cs, "not setting the F flag; " "have %zd, need %zd", off, (size_t)io->scsiio.kern_total_len); +#endif } KASSERT(response->ip_data_len > 0, ("sending empty Data-In")); cfiscsi_pdu_queue(response);