File: bu.sh
   1 #!/bin/sh
   2 
   3 # The MIT License (MIT)
   4 #
   5 # Copyright © 2024 pacman64
   6 #
   7 # Permission is hereby granted, free of charge, to any person obtaining a copy
   8 # of this software and associated documentation files (the “Software”), to deal
   9 # in the Software without restriction, including without limitation the rights
  10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 # copies of the Software, and to permit persons to whom the Software is
  12 # furnished to do so, subject to the following conditions:
  13 #
  14 # The above copyright notice and this permission notice shall be included in
  15 # all copies or substantial portions of the Software.
  16 #
  17 # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23 # SOFTWARE.
  24 
  25 
  26 # bu [time]
  27 # bu [quantity] [unit]
  28 # bu [quantity] ft [quantity] in
  29 # bu [quantity] lb [quantity] oz
  30 #
  31 # Better Units
  32 #
  33 #
  34 # Turn common measurement units used in the US into their international
  35 # counterparts. Even the mixed-unit cases ft-in and lb-oz are accepted,
  36 # when given 2 number-unit pairs.
  37 #
  38 # Besides those, time units in common use, such as minutes, hours, days,
  39 # and weeks have been added for convenience: these are all turned into
  40 # seconds.
  41 #
  42 # Even time-formats HH:MM:SS and MM:SS are supported: the seconds part
  43 # can optionally have decimal digits, and no unit is needed, as those
  44 # formats clearly identify their time-related units by themselves.
  45 #
  46 #
  47 # You may also want to use the shortcut below, instead of using this
  48 # script directly.
  49 #
  50 # # Change Units converts common units into more convenient equivalents
  51 # cu() {
  52 #     ( set -o pipefail && bu "$@" | awk '{ print $(NF-1), $NF }' )
  53 # }
  54 
  55 
  56 # handle help options
  57 case "$1" in
  58     -h|--h|-help|--help)
  59         # show help message, extracting the info-comment at the start
  60         # of this file, and quit
  61         awk '/^# +bu/, /^$/ { gsub(/^# ?/, ""); print }' "$0"
  62         exit 0
  63     ;;
  64 esac
  65 
  66 # normalize the unit name right away, to avoid re-running the same apps
  67 unitname="$(echo ${2} | tr [:upper:] [:lower:] | tr -d - | tr -d _)"
  68 
  69 case "${unitname}" in
  70     # handle that mixed-unit abomination using feet and inches
  71     ft|feet|foot)
  72         case "$(echo ${4} | tr [:upper:] [:lower:])" in
  73             in|inch|inches)
  74                 y=$(echo "scale=4; 0.3048*${1} + 0.0254*${3}" | bc)
  75                 printf "%12.4f ft %12.4f in = %12.4f m\n" \
  76                     "${1}" "${3}" "${y}"
  77                 exit $?
  78             ;;
  79         esac
  80     ;;
  81 
  82     # handle hertz, megahertz, and gigahertz: even some old-fashioned names
  83     # are supported; the name `cycle` is handled as an angular unit instead
  84     hz|hertz)
  85         y=$(echo "scale=4; 1/${1}" | bc)
  86         printf "%12.4f hz = %12.4f s\n" "${1}" "${y}"
  87         exit $?
  88     ;;
  89     mhz|megahertz|megacycle|megacycles)
  90         y=$(echo "scale=12; 1/(1000000*${1})" | bc)
  91         printf "%12.4f hz = %12.12f s\n" "${1}" "${y}"
  92         exit $?
  93     ;;
  94     ghz|gigahertz|gigacycle|gigacycles)
  95         y=$(echo "scale=12; 1/(1000000000*${1})" | bc)
  96         printf "%12.4f hz = %12.12f s\n" "${1}" "${y}"
  97         exit $?
  98     ;;
  99 
 100     # handle that mixed-unit abomination using pounds and weight-ounces
 101     lb|lbs|pound|pounds)
 102         case "$(echo ${4} | tr [:upper:] [:lower:])" in
 103             oz|ozs|ounce|ounces)
 104                 y=$(echo "scale=4; 0.45359237*${1} + 0.028349523*${3}" | bc)
 105                 printf "%12.4f lb %12.4f oz = %12.4f kg\n" \
 106                     "${1}" "${3}" "${y}"
 107                 exit $?
 108             ;;
 109         esac
 110     ;;
 111 
 112     # handle converting numbers from base 2 into base-10 ones
 113     bin|binary|base2|b2|0b|bit|bits|2)
 114         # check for valid binary values, quitting whole script on failure
 115         if echo "$1" | grep -E -q '.*[^01].*'; then
 116             printf "\x1b[31minvalid binary number %s\x1b[0m\n" "$1" \
 117                 > "/dev/stderr"
 118             exit 1
 119         fi
 120 
 121         decval=$(echo "ibase=2; ${1}" | bc)
 122         printf "%s bin = %s dec\n" "${1}" "${decval}"
 123         exit $?
 124     ;;
 125 
 126     # handle converting numbers from base 8 into base-10 ones
 127     oct|octal|base8|b8|0o|o|8)
 128         # check for valid binary values, quitting whole script on failure
 129         if echo "$1" | grep -E -q '.*[^0-7].*'; then
 130             printf "\x1b[31minvalid octal number %s\x1b[0m\n" "$1" \
 131                 > "/dev/stderr"
 132             exit 1
 133         fi
 134 
 135         decval=$(echo "ibase=8; ${1}" | bc)
 136         printf "%s oct = %s dec\n" "${1}" "${decval}"
 137         exit $?
 138     ;;
 139 
 140     # handle converting numbers from base 16 into base-10 ones
 141     hex|hexa|hexadec|hexadecimal|base16|b16|0x|x|16)
 142         hexval=$(echo "${1}" | sed 's/^0x//' | tr '[:lower:]' '[:upper:]')
 143         if echo "${hexval}" | grep -E -q '.*[^0-9a-fA-F].*'; then
 144             printf "\x1b[31minvalid octal number %s\x1b[0m\n" "$1" \
 145                 > "/dev/stderr"
 146             exit 1
 147         fi
 148 
 149         decval=$(echo "ibase=16; ${hexval}" | bc)
 150         printf "%s hex = %s dec\n" "${hexval}" "${decval}"
 151         exit $?
 152     ;;
 153 esac
 154 
 155 # warn users who might wrongly think multiple pairs of args are allowed
 156 # in general: only ft/in and lb/oz are allowed
 157 if [ $# -gt 2 ]; then
 158     printf "\x1b[31m" >&2
 159     printf "only 1 or 2 args allowed, unless using ft/in or " >&2
 160     printf "lb/oz mixed-units value-pairs" >&2
 161     printf "\x1b[0m\n" >&2
 162     exit 1
 163 fi
 164 
 165 # handle all other unit-conversions
 166 awk -v x="${1}" -v unit="${unitname}" '
 167 # normalize both quantities and units, and handle special cases for units
 168 BEGIN {
 169     # ignore underscores in numbers, thus `allowing` them for convenience
 170     gsub(/[_]/, "", x)
 171 
 172     # normalize unit names for later lookup
 173     unit = tolower(unit)
 174     gsub(/[ _.-]/, "", unit)
 175 
 176     # handle fahrenheit temperatures
 177     if (unit == "f" || unit == "fahrenheit") {
 178         printf "%12.4f °F = %12.4f °C\n", x, (x - 32) / (5.0/9.0)
 179         ok = 1
 180         exit 0
 181     }
 182 
 183     # handle kelvin temperatures
 184     if (unit == "k" || unit == "kelvin") {
 185         printf "%12.4f °K = %12.4f °C\n", x, x - 273.15
 186         ok = 1
 187         exit 0
 188     }
 189 
 190     # handle the mm:ss time format
 191     if (match(x, /^[0-9]+:[0-9]{1,2}(\.[0-9]*)?$/)) {
 192         split(x, hms, ":")
 193         printf "%s = %12.4f s\n", x, 60*hms[1] + hms[2]
 194         ok = 1
 195         exit 0
 196     }
 197 
 198     # handle the hh:mm:ss time format
 199     if (match(x, /^[0-9]+:[0-9]{1,2}:[0-9]{1,2}(\.[0-9]*)?$/)) {
 200         split(x, hms, ":")
 201         printf "%s = %12.4f s\n", x, 3600*hms[1] + 60*hms[2] + hms[3]
 202         ok = 1
 203         exit 0
 204     }
 205 
 206     # handle seconds, turning the number into the hh:mm:ss time format
 207     if (unit == "s") {
 208         h = (x - x % 3600) / 3600
 209         m = (x % 3600) / 60
 210         s = x % 60
 211         printf "%s s = %02d:%02d:%05.2f\n", x, h, m, s
 212         ok = 1
 213         exit 0
 214     }
 215 }
 216 
 217 # match the unit given to its built-in entry, if present
 218 $1 == unit {
 219     printf "%12.4f %s = %12.4f %s\n", x, $2, $3*x, $4
 220     ok = 1
 221     exit 0
 222 }
 223 
 224 # tell user when no match was found, and fail the whole surrounding tool if so
 225 END {
 226     if (!ok) {
 227         fs = "\x1b[31munit name/alias `%s` not supported\n\x1b[0m"
 228         printf fs, unit > "/dev/stderr"
 229         exit 1
 230     }
 231 }
 232 ' << 'EOF'
 233 ac              ac    4046.8564224     m²
 234 acre            ac    4046.8564224     m²
 235 acres           ac    4046.8564224     m²
 236 angle           deg   0.0174532925199  rad
 237 andeg           deg   0.0174532925199  rad
 238 angdeg          deg   0.0174532925199  rad
 239 angular         deg   0.0174532925199  rad
 240 angulardeg      deg   0.0174532925199  rad
 241 angulardegree   deg   0.0174532925199  rad
 242 angulardegrees  deg   0.0174532925199  rad
 243 angledeg        deg   0.0174532925199  rad
 244 angledegree     deg   0.0174532925199  rad
 245 angledegrees    deg   0.0174532925199  rad
 246 barrel          bbl   158.9873         L
 247 barrels         bbl   158.9873         L
 248 bbl             bbl   158.9873         L
 249 cfeet           ft³   0.028316846592   m³
 250 cfoot           ft³   0.028316846592   m³
 251 cft             ft³   0.028316846592   m³
 252 cufeet          ft³   0.028316846592   m³
 253 cufoot          ft³   0.028316846592   m³
 254 cuft            ft³   0.028316846592   m³
 255 cup             cup   0.23658824       L
 256 cups            cup   0.23658824       L
 257 cyc             tr    6.2831853071796  rad
 258 cycle           tr    6.2831853071796  rad
 259 cycles          tr    6.2831853071796  rad
 260 d               day   86400            s
 261 day             day   86400            s
 262 days            day   86400            s
 263 deg             deg   0.0174532925199  rad
 264 degree          deg   0.0174532925199  rad
 265 degrees         deg   0.0174532925199  rad
 266 in              in    2.54             cm
 267 inch            in    2.54             cm
 268 inches          in    2.54             cm
 269 ft              ft    0.3048           m
 270 ft2             ft²   0.09290304       m²
 271 ft3             ft³   0.028316846592   m³
 272 ft²             ft²   0.09290304       m²
 273 ft³             ft³   0.028316846592   m³
 274 feet            ft    0.3048           m
 275 feet2           ft²   0.09290304       m²
 276 feet3           ft³   0.028316846592   m³
 277 feet²           ft²   0.09290304       m²
 278 feet³           ft³   0.028316846592   m³
 279 floz            floz  29.5735295625    mL
 280 foot            ft    0.3048           m
 281 foot2           ft²   0.09290304       m²
 282 foot3           ft³   0.028316846592   m³
 283 foot²           ft²   0.09290304       m²
 284 foot³           ft³   0.028316846592   m³
 285 ga              gal   3.785411784      L
 286 gal             gal   3.785411784      L
 287 gallon          gal   3.785411784      L
 288 gallons         gal   3.785411784      L
 289 gb              gb    1073741824       bytes
 290 gib             gb    1073741824       bytes
 291 gon             grad  0.0157079632679  rad
 292 gons            grad  0.0157079632679  rad
 293 grad            grad  0.0157079632679  rad
 294 gradian         grad  0.0157079632679  rad
 295 gradians        grad  0.0157079632679  rad
 296 h               hr    3600             s
 297 hour            hr    3600             s
 298 hours           hr    3600             s
 299 hr              hr    3600             s
 300 hrs             hr    3600             s
 301 kb              kb    1024             bytes
 302 kib             kb    1024             bytes
 303 lb              lb    0.45359237       kg
 304 lbs             lb    0.45359237       kg
 305 mb              mb    1048576          bytes
 306 mi              mi    1.609344         km
 307 mib             mb    1048576          bytes
 308 mih             mph   1.609344         kph
 309 mi2             mi²   2.5899881103360  km²
 310 mi²             mi²   2.5899881103360  km²
 311 mil             mrad  0.001            rad
 312 millirad        mrad  0.001            rad
 313 milliradian     mrad  0.001            rad
 314 milliradians    mrad  0.001            rad
 315 mile            mi    1.609344         km
 316 mile²           mi²   2.5899881103360  km²
 317 miles           mi    1.609344         km
 318 miles²          mi²   2.5899881103360  km²
 319 min             min   60               s
 320 minute          min   60               s
 321 minutes         min   60               s
 322 mpg             mpg   0.425143707      kpl
 323 mph             mph   1.609344         kph
 324 mrad            mrad  0.001            rad
 325 nmi             nmi   1.852            km
 326 nmile           nmi   1.852            km
 327 nmiles          nmi   1.852            km
 328 oilbarrel       bbl   158.9873         L
 329 oilbarrels      bbl   158.9873         L
 330 oz              oz    28.349523125     g
 331 ounce           oz    28.349523125     g
 332 ounces          oz    28.349523125     g
 333 pint            pt    473.176473       mL
 334 pla             tr    6.2831853071796  rad
 335 pound           lb    0.45359237       kg
 336 pounds          lb    0.45359237       kg
 337 psi             psi   6894.757293168   Pa
 338 pt              pt    473.176473       mL
 339 rad             rad   57.295779513082  deg
 340 radian          rad   57.295779513082  deg
 341 radians         rad   57.295779513082  deg
 342 rev             tr    6.2831853071796  rad
 343 revolution      tr    6.2831853071796  rad
 344 revolutions     tr    6.2831853071796  rad
 345 sfeet           ft²   0.09290304       m²
 346 sfoot           ft²   0.09290304       m²
 347 sft             ft²   0.09290304       m²
 348 smi             mi²   2.5899881103360  km²
 349 smile           mi²   2.5899881103360  km²
 350 smiles          mi²   2.5899881103360  km²
 351 syard           yd²   0.83612736       m²
 352 syards          yd²   0.83612736       m²
 353 syd             yd²   0.83612736       m²
 354 syds            yd²   0.83612736       m²
 355 sqfeet          ft²   0.09290304       m²
 356 sqfoot          ft²   0.09290304       m²
 357 sqft            ft²   0.09290304       m²
 358 sqmi            mi²   2.5899881103360  km²
 359 sqmile          mi²   2.5899881103360  km²
 360 sqmiles         mi²   2.5899881103360  km²
 361 sqyard          yd²   0.83612736       m²
 362 sqyards         yd²   0.83612736       m²
 363 sqyd            yd²   0.83612736       m²
 364 sqyds           yd²   0.83612736       m²
 365 tb              tb    1099511627776    bytes
 366 tib             tb    1099511627776    bytes
 367 ton             ton   907.18474        kg
 368 tr              tr    6.2831853071796  rad
 369 turn            tr    6.2831853071796  rad
 370 turns           tr    6.2831853071796  rad
 371 ukpint          ukpt  568.26125        mL
 372 ukpt            ukpt  568.26125        mL
 373 uspint          pt    473.176473       mL
 374 uspt            pt    473.176473       mL
 375 w               wk    604800           s
 376 week            wk    604800           s
 377 weeks           wk    604800           s
 378 wk              wk    604800           s
 379 wks             wk    604800           s
 380 yard            yd    0.9144           m
 381 yard2           yd²   0.83612736       m²
 382 yards           yd    0.9144           m
 383 yards2          yd²   0.83612736       m²
 384 yd              yd    0.9144           m
 385 yds             yd    0.9144           m
 386 yd²             yd²   0.83612736       m²
 387 yds²            yd²   0.83612736       m²
 388 EOF