Int stack implementation
Things you did well:


Overall the code looks very nice and well organized.
You used comments well.


Things you could improve:

Syntax:


typedef your structs.


struct Stack {
  int value;
  int is_empty;
  struct Stack *next;
};



The typedef means you no longer have to write struct all over the place. That not only saves some space, it also can make the code cleaner since it provides a bit more abstraction.

typedef struct
{
    int value;
    int is_empty;
    struct Stack *next;
} Stack;

You can simplify your NULL checks.


if (head != NULL)



Since NULL is defined as (void *)0, we can treat is as a comparison to 0, making the != NULL a redundant check.

if (head)

If you don't take in any variables as parameters, you should declare them as void.

int main(void)



Error handling:


Don't use abort().  The function raises the SIGABRT signal. This, if uncaught, causes the program to terminate returning a platform-dependent unsuccessful termination error code to the host environment. The program is terminated without destroying any object and without calling any of the functions passed to atexit or at_quick_exit.  It would be much better to return an error indicator and have the calling function handle it.

return -1; // we have an error



Memory usage:


You never free() your init value.


struct Stack *init = empty();

// within empty()
struct Stack *head = malloc(sizeof(struct Stack *));
return *head;



You should probably free() it before your program terminates.  Failure to deallocate memory using free() leads to buildup of non-reusable memory, which is no longer used by the program. This wastes memory resources and can lead to allocation failures when these resources are exhausted.

free(init);



Returning:


You don't return a value from main(), even though you state you do.


int main() {
    // no return value... no indication of successful exit
}



Returning 0 is usually a typical indication of a successful exit.

return 0;