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         awk '/^# +bu/, /^$/ { gsub(/^# ?/, ""); print }' "$0"
  60         exit 0
  61     ;;
  62 esac
  63 
  64 # normalize the unit name right away, to avoid re-running the same apps
  65 unitname="$(echo ${2} | tr [:upper:] [:lower:] | tr -d - | tr -d _)"
  66 
  67 case "${unitname}" in
  68     # handle that mixed-unit abomination using feet and inches
  69     ft|feet|foot)
  70         case "$(echo ${4} | tr [:upper:] [:lower:])" in
  71             in|inch|inches)
  72                 y=$(echo "scale=4; 0.3048*${1} + 0.0254*${3}" | bc)
  73                 printf "%12.4f ft %12.4f in = %12.4f m\n" \
  74                     "${1}" "${3}" "${y}"
  75                 exit $?
  76             ;;
  77         esac
  78     ;;
  79 
  80     # handle hertz, megahertz, and gigahertz: even some old-fashioned names
  81     # are supported; the name `cycle` is handled as an angular unit instead
  82     hz|hertz)
  83         y=$(echo "scale=4; 1/${1}" | bc)
  84         printf "%12.4f hz = %12.4f s\n" "${1}" "${y}"
  85         exit $?
  86     ;;
  87     mhz|megahertz|megacycle|megacycles)
  88         y=$(echo "scale=12; 1/(1000000*${1})" | bc)
  89         printf "%12.4f hz = %12.12f s\n" "${1}" "${y}"
  90         exit $?
  91     ;;
  92     ghz|gigahertz|gigacycle|gigacycles)
  93         y=$(echo "scale=12; 1/(1000000000*${1})" | bc)
  94         printf "%12.4f hz = %12.12f s\n" "${1}" "${y}"
  95         exit $?
  96     ;;
  97 
  98     # handle that mixed-unit abomination using pounds and weight-ounces
  99     lb|lbs|pound|pounds)
 100         case "$(echo ${4} | tr [:upper:] [:lower:])" in
 101             oz|ozs|ounce|ounces)
 102                 y=$(echo "scale=4; 0.45359237*${1} + 0.028349523*${3}" | bc)
 103                 printf "%12.4f lb %12.4f oz = %12.4f kg\n" \
 104                     "${1}" "${3}" "${y}"
 105                 exit $?
 106             ;;
 107         esac
 108     ;;
 109 
 110     # handle converting numbers from base 2 into base-10 ones
 111     bin|binary|base2|b2|0b|bit|bits|2)
 112         # check for valid binary values, quitting whole script on failure
 113         if echo "$1" | grep -E -q '.*[^01].*'; then
 114             printf "\x1b[31minvalid binary number %s\x1b[0m\n" "$1" \
 115                 > "/dev/stderr"
 116             exit 1
 117         fi
 118 
 119         decval=$(echo "ibase=2; ${1}" | bc)
 120         printf "%s bin = %s dec\n" "${1}" "${decval}"
 121         exit $?
 122     ;;
 123 
 124     # handle converting numbers from base 8 into base-10 ones
 125     oct|octal|base8|b8|0o|o|8)
 126         # check for valid binary values, quitting whole script on failure
 127         if echo "$1" | grep -E -q '.*[^0-7].*'; then
 128             printf "\x1b[31minvalid octal number %s\x1b[0m\n" "$1" \
 129                 > "/dev/stderr"
 130             exit 1
 131         fi
 132 
 133         decval=$(echo "ibase=8; ${1}" | bc)
 134         printf "%s oct = %s dec\n" "${1}" "${decval}"
 135         exit $?
 136     ;;
 137 
 138     # handle converting numbers from base 16 into base-10 ones
 139     hex|hexa|hexadec|hexadecimal|base16|b16|0x|x|16)
 140         hexval=$(echo "${1}" | sed 's-^0x--' | tr '[:lower:]' '[:upper:]')
 141         if echo "${hexval}" | grep -E -q '.*[^0-9a-fA-F].*'; then
 142             printf "\x1b[31minvalid octal number %s\x1b[0m\n" "$1" \
 143                 > "/dev/stderr"
 144             exit 1
 145         fi
 146 
 147         decval=$(echo "ibase=16; ${hexval}" | bc)
 148         printf "%s hex = %s dec\n" "${hexval}" "${decval}"
 149         exit $?
 150     ;;
 151 esac
 152 
 153 # warn users who might wrongly think multiple pairs of args are allowed
 154 # in general: only ft/in and lb/oz are allowed
 155 if [ $# -gt 2 ]; then
 156     printf "\x1b[31m" >&2
 157     printf "only 1 or 2 args allowed, unless using ft/in or " >&2
 158     printf "lb/oz mixed-units value-pairs" >&2
 159     printf "\x1b[0m\n" >&2
 160     exit 1
 161 fi
 162 
 163 # handle all other unit-conversions
 164 awk -v x="${1}" -v unit="${unitname}" '
 165 # normalize both quantities and units, and handle special cases for units
 166 BEGIN {
 167     # ignore underscores in numbers, thus `allowing` them for convenience
 168     gsub(/[_]/, "", x)
 169 
 170     # normalize unit names for later lookup
 171     unit = tolower(unit)
 172     gsub(/[ _.-]/, "", unit)
 173 
 174     # handle fahrenheit temperatures
 175     if (unit == "f" || unit == "fahrenheit") {
 176         printf "%12.4f °F = %12.4f °C\n", x, (x - 32) / (5.0/9.0)
 177         ok = 1
 178         exit 0
 179     }
 180 
 181     # handle kelvin temperatures
 182     if (unit == "k" || unit == "kelvin") {
 183         printf "%12.4f °K = %12.4f °C\n", x, x - 273.15
 184         ok = 1
 185         exit 0
 186     }
 187 
 188     # handle the mm:ss time format
 189     if (match(x, /^[0-9]+:[0-9]{1,2}(\.[0-9]*)?$/)) {
 190         split(x, hms, ":")
 191         printf "%s = %12.4f s\n", x, 60*hms[1] + hms[2]
 192         ok = 1
 193         exit 0
 194     }
 195 
 196     # handle the hh:mm:ss time format
 197     if (match(x, /^[0-9]+:[0-9]{1,2}:[0-9]{1,2}(\.[0-9]*)?$/)) {
 198         split(x, hms, ":")
 199         printf "%s = %12.4f s\n", x, 3600*hms[1] + 60*hms[2] + hms[3]
 200         ok = 1
 201         exit 0
 202     }
 203 
 204     # handle seconds, turning the number into the hh:mm:ss time format
 205     if (unit == "s") {
 206         h = (x - x % 3600) / 3600
 207         m = (x % 3600) / 60
 208         s = x % 60
 209         printf "%s s = %02d:%02d:%05.2f\n", x, h, m, s
 210         ok = 1
 211         exit 0
 212     }
 213 }
 214 
 215 # match the unit given to its built-in entry, if present
 216 $1 == unit {
 217     printf "%12.4f %s = %12.4f %s\n", x, $2, $3*x, $4
 218     ok = 1
 219     exit 0
 220 }
 221 
 222 # tell user when no match was found, and fail the whole surrounding tool if so
 223 END {
 224     if (!ok) {
 225         fs = "\x1b[31munit name/alias `%s` not supported\n\x1b[0m"
 226         printf fs, unit > "/dev/stderr"
 227         exit 1
 228     }
 229 }
 230 ' << 'EOF'
 231 ac              ac    4046.8564224     m²
 232 acre            ac    4046.8564224     m²
 233 acres           ac    4046.8564224     m²
 234 angle           deg   0.0174532925199  rad
 235 andeg           deg   0.0174532925199  rad
 236 angdeg          deg   0.0174532925199  rad
 237 angular         deg   0.0174532925199  rad
 238 angulardeg      deg   0.0174532925199  rad
 239 angulardegree   deg   0.0174532925199  rad
 240 angulardegrees  deg   0.0174532925199  rad
 241 angledeg        deg   0.0174532925199  rad
 242 angledegree     deg   0.0174532925199  rad
 243 angledegrees    deg   0.0174532925199  rad
 244 barrel          bbl   158.9873         L
 245 barrels         bbl   158.9873         L
 246 bbl             bbl   158.9873         L
 247 cfeet           ft³   0.028316846592   m³
 248 cfoot           ft³   0.028316846592   m³
 249 cft             ft³   0.028316846592   m³
 250 cufeet          ft³   0.028316846592   m³
 251 cufoot          ft³   0.028316846592   m³
 252 cuft            ft³   0.028316846592   m³
 253 cup             cup   0.23658824       L
 254 cups            cup   0.23658824       L
 255 cyc             tr    6.2831853071796  rad
 256 cycle           tr    6.2831853071796  rad
 257 cycles          tr    6.2831853071796  rad
 258 d               day   86400            s
 259 day             day   86400            s
 260 days            day   86400            s
 261 deg             deg   0.0174532925199  rad
 262 degree          deg   0.0174532925199  rad
 263 degrees         deg   0.0174532925199  rad
 264 in              in    2.54             cm
 265 inch            in    2.54             cm
 266 inches          in    2.54             cm
 267 ft              ft    0.3048           m
 268 ft2             ft²   0.09290304       m²
 269 ft3             ft³   0.028316846592   m³
 270 ft²             ft²   0.09290304       m²
 271 ft³             ft³   0.028316846592   m³
 272 feet            ft    0.3048           m
 273 feet2           ft²   0.09290304       m²
 274 feet3           ft³   0.028316846592   m³
 275 feet²           ft²   0.09290304       m²
 276 feet³           ft³   0.028316846592   m³
 277 floz            floz  29.5735295625    mL
 278 foot            ft    0.3048           m
 279 foot2           ft²   0.09290304       m²
 280 foot3           ft³   0.028316846592   m³
 281 foot²           ft²   0.09290304       m²
 282 foot³           ft³   0.028316846592   m³
 283 ga              gal   3.785411784      L
 284 gal             gal   3.785411784      L
 285 gallon          gal   3.785411784      L
 286 gallons         gal   3.785411784      L
 287 gb              gb    1073741824       bytes
 288 gib             gb    1073741824       bytes
 289 gon             grad  0.0157079632679  rad
 290 gons            grad  0.0157079632679  rad
 291 grad            grad  0.0157079632679  rad
 292 gradian         grad  0.0157079632679  rad
 293 gradians        grad  0.0157079632679  rad
 294 h               hr    3600             s
 295 hour            hr    3600             s
 296 hours           hr    3600             s
 297 hr              hr    3600             s
 298 hrs             hr    3600             s
 299 kb              kb    1024             bytes
 300 kib             kb    1024             bytes
 301 lb              lb    0.45359237       kg
 302 lbs             lb    0.45359237       kg
 303 mb              mb    1048576          bytes
 304 mi              mi    1.609344         km
 305 mib             mb    1048576          bytes
 306 mih             mph   1.609344         kph
 307 mi2             mi²   2.5899881103360  km²
 308 mi²             mi²   2.5899881103360  km²
 309 mil             mrad  0.001            rad
 310 millirad        mrad  0.001            rad
 311 milliradian     mrad  0.001            rad
 312 milliradians    mrad  0.001            rad
 313 mile            mi    1.609344         km
 314 mile²           mi²   2.5899881103360  km²
 315 miles           mi    1.609344         km
 316 miles²          mi²   2.5899881103360  km²
 317 min             min   60               s
 318 minute          min   60               s
 319 minutes         min   60               s
 320 mpg             mpg   0.425143707      kpl
 321 mph             mph   1.609344         kph
 322 mrad            mrad  0.001            rad
 323 nmi             nmi   1.852            km
 324 nmile           nmi   1.852            km
 325 nmiles          nmi   1.852            km
 326 oilbarrel       bbl   158.9873         L
 327 oilbarrels      bbl   158.9873         L
 328 oz              oz    28.349523125     g
 329 ounce           oz    28.349523125     g
 330 ounces          oz    28.349523125     g
 331 pint            pt    473.176473       mL
 332 pla             tr    6.2831853071796  rad
 333 pound           lb    0.45359237       kg
 334 pounds          lb    0.45359237       kg
 335 psi             psi   6894.757293168   Pa
 336 pt              pt    473.176473       mL
 337 rad             rad   57.295779513082  deg
 338 radian          rad   57.295779513082  deg
 339 radians         rad   57.295779513082  deg
 340 rev             tr    6.2831853071796  rad
 341 revolution      tr    6.2831853071796  rad
 342 revolutions     tr    6.2831853071796  rad
 343 sfeet           ft²   0.09290304       m²
 344 sfoot           ft²   0.09290304       m²
 345 sft             ft²   0.09290304       m²
 346 smi             mi²   2.5899881103360  km²
 347 smile           mi²   2.5899881103360  km²
 348 smiles          mi²   2.5899881103360  km²
 349 syard           yd²   0.83612736       m²
 350 syards          yd²   0.83612736       m²
 351 syd             yd²   0.83612736       m²
 352 syds            yd²   0.83612736       m²
 353 sqfeet          ft²   0.09290304       m²
 354 sqfoot          ft²   0.09290304       m²
 355 sqft            ft²   0.09290304       m²
 356 sqmi            mi²   2.5899881103360  km²
 357 sqmile          mi²   2.5899881103360  km²
 358 sqmiles         mi²   2.5899881103360  km²
 359 sqyard          yd²   0.83612736       m²
 360 sqyards         yd²   0.83612736       m²
 361 sqyd            yd²   0.83612736       m²
 362 sqyds           yd²   0.83612736       m²
 363 tb              tb    1099511627776    bytes
 364 tib             tb    1099511627776    bytes
 365 ton             ton   907.18474        kg
 366 tr              tr    6.2831853071796  rad
 367 turn            tr    6.2831853071796  rad
 368 turns           tr    6.2831853071796  rad
 369 ukpint          ukpt  568.26125        mL
 370 ukpt            ukpt  568.26125        mL
 371 uspint          pt    473.176473       mL
 372 uspt            pt    473.176473       mL
 373 w               wk    604800           s
 374 week            wk    604800           s
 375 weeks           wk    604800           s
 376 wk              wk    604800           s
 377 wks             wk    604800           s
 378 yard            yd    0.9144           m
 379 yard2           yd²   0.83612736       m²
 380 yards           yd    0.9144           m
 381 yards2          yd²   0.83612736       m²
 382 yd              yd    0.9144           m
 383 yds             yd    0.9144           m
 384 yd²             yd²   0.83612736       m²
 385 yds²            yd²   0.83612736       m²
 386 EOF