Base64 encoding implementation
funcs.h

Neither funcs.c nor funcs.h require anything from stdio.h or string.h, so you shouldn't be including those there, move them to main.c instead. But you should get into the habit of adding include guards to your headers. So either use this at the top:

#pragma once


Or, to be completely standard and portable:

#ifndef FUNCS_H
#define FUNCS_H

// contents of header here

#endif // FUNCS_H


funcs.c

The static encode mapping is fine as you have it, but would also be fine inside the get_index_digit function (static also of course). Since that is the only direct user, it would be slightly better inside in my opinion.

I'd change your signature and preamble for the encoder to:

#include &lt;assert.h&gt;
// ...

char get_base64_digit(const unsigned int base64, unsigned int digit_number)
{
  assert(digit_number &lt; 4);
  // ...


Passing a negative index or anything greater than 3 is a programming error, it should be caught as early as possible and be a hard failure.

You can get rid of the switch by collapsing all cases:

return get_index_digit((base64 &gt;&gt; (18 - 6*digit_number)) &amp; 077);


Note that this masks the bits even for the zero case, which is safer. But you might want instead to assert on the fact that the bits above index 18 are all zeros - slightly different contract. (I'd prefer just masking them off though.)

main.c

char* text = argc == 2 ? argv[1] : '\0';


That '\0' is bad. Happens to work, but doesn't convey the right information. text is a pointer, not a char. Replace with:

char* text = argc == 2 ? argv[1] : NULL;


But that's actually not a good idea either. What you actually need is an empty string (or bail out).

const char* text = argc == 2 ? argv[1] : "";


Then you have:

unsigned final, temp;


But you're not using temp anywhere, except for setting it to zero. And you're always initializing final to zero in the for loop. So to help simplifying that for a bit:

unsigned final = 0;


Also a slight inconsistency: you're using unsigned here, but used unsigned int in the declaration for get_base64_digit - pick one version and stick to that (they are completely identical).

As for the loop:

for (int x = final = temp = 0; x &lt; (remainder == 0 ? text_len : text_len - remainder); x += 3) {


That line is too long, and more complex than it needs to be. Removing final and temp will help:

for (int x = 0; x &lt; (remainder == 0 ? text_len : text_len - remainder); x += 3) {


And might help you to see that the conditional is unnecessary. If remainder is zero, text_len and text_len - remainder have the same value. So:

for (int x = 0; x &lt; text_len - remainder; x += 3) {


Or move that subtraction out of the loop for an even simpler structure.

The last lines are a bit odd:

} else
        return 0;

return 0;


Remove the else, it is not useful. Also mixing braced and non-braced blocks in an if/else chain isn't a good idea. This is a matter of style, but always using braces is usually safer. If you want to omit them for single-statement blocks, only do that if you only have single-statement blocks in the if/else chain.

Last tidbit: you sometimes have ){ (no space), and sometimes ) { (one space). Stick to one, preferably the one with the space. You also sometimes have an empty line at the start of blocks, sometimes not. If you feel like a bit of vertical separation is good there, try putting the { that starts the block on its own line (as I did in the get_base64_digit above), and look at the Linux coding style guidelines for some other tips that you may, or may not :-), like.

clang-format is an excellent auto-format tool, and a lot of dev tools (including Vim and Emacs) can be made to use it. Try it out.