Search code examples
cbashbase64built-in

Translate base64 encoder C source code to bash builtins script


Good day Stack you may think that this is the same question that has been answered before about the old base64, xxd, hexdump combination of commands to convert strings into base64, but instead of using external commands i'm trying to do the same only using builtins from bash like printf, read, echo... i am translating a C source from here and i have the encoding working so far but i have stumbled into a problematic C line

char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
...
char *p;
...
p = strchr(b64, c);
...
in[phase] = p - b64; //Problematic line

The thing is that p and b64 are char arrays and i don't understand what's doing the arithmetic expression with the two char arrays to obtain a number, it could be something like not or xor O_o. I'm still learning C and i don't know a lot.

This is what i have so far:

#!/bin/bash

declare -r b64=( {A..Z} {a..z} {0..9} {+,/} )

function ord() {
    for i in {0..3}; do
        printf '%d ' "'${1:$i:1}"
    done
}

#TODO
function decodeblock() {
    local out in=( $(ord $1) )
    out[0]=$(( ${in[0]} << 2 | ${in[1]} >> 4 ))
    out[1]=$(( ${in[1]} << 4 | ${in[2]} >> 2 ))
    out[2]=$(( ${in[2]} << 6 | ${in[3]} >> 0 ))
    printf "%s" "${out[@]}"
}

function encodeblock() {
    local in=( $(ord "$1") ) len=$2 out index
    index=$(( ${in[0]} >> 2 ))
    out[0]=${b64[$index]}
    index=$(( ((${in[0]} & 0x03) << 4) | ((${in[1]} & 0xf0) >> 4) ))
    out[1]=${b64[$index]}
    index=$(( ((${in[1]} & 0x0f) << 2) | ((${in[2]} & 0xc0) >> 6) ))
    out[2]=$( (($len > 1)) && echo -n ${b64[$index]} || echo -n '=' )
    index=$(( ${in[2]} & 0x3f ))
    out[3]=$( (($len > 2)) && echo -n ${b64[$index]} || echo -n '=' )
    printf "%s" "${out[@]}"
}

function b64_decode() {
    local c i=0 phase=0 block p b64src="$1"
    for (( i = 0 ; i < ${#b64src} ; i++)); do
        c="${b64src:$i:1}"
        [[ "$c" == '=' ]] && decodeblock "$b64src" && break
        p="$c${b64[@]##*$c}"
        if [[ -n "$p" ]]; then
            #TODO
            b64src[$phase]=
            phase=$(( ($phase + 1) % 4 ))
            if [[ $phase -eq 0 ]]; then
                decodeblock "$b64src"
                b64src=''
            fi
            ((i++))
        fi
    done
}

function b64_encode() {
    local block j=0 len clrstr="$1"
    while [[ -n "${clrstr:$j:1}" ]]; do
        len=0
        for (( i = 0 ; i < 3 ; i++ )); do
            block+="${clrstr:$j:1}"
            [[ -n "${clrstr:$j:1}" ]] && { ((len++)) ; ((j++)); }
        done
        [[ -n $len ]] && encodeblock "${block[@]}" $len
        block='' ; len=0
    done
}

echo -n 'hello world' | base64 #DEBUG
b64_encode 'hello world' #DEBUG

b64_decode "$( b64_encode 'hello world' )"

Regards and thanks.


Solution

  • strchr(b64, c) searches the character c into the string b64 and returns a pointer to it if found; subtracting b64 from this value simply yields the index of the character inside b64.

    Long story short it's getting the index of c in b64. As for how to implement this in the shell, here you may find some inspiration.