C stack (linked list)

A few years ago (2008), I started a holiday tradition: do some serious coding over the break. This break - it was C - specifically pointers and structs. Armed with "C Programming (1988)" and time, I found some bit on wikipedia about linked lists. So I inefficiently made an implementation of tac in C, using linked lists.

stacklib.h

#define MAXLINE 1024
#define MAXSTACK 40960 

typedef struct stack {
    int index;
    char type[10];
    long datalen;
    void *data;
    struct stack *next;
} STACK;  /* STACK aka 'struct stack' */
/* http://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29 */

void push(STACK **head, char *type, void *data, int *index);
int peek (STACK *head);
int structsize (void);
void pop(STACK *head);

stacklib.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stacklib.h"

extern int verbose;

/* stack lib */

void push(STACK **head, char *type, void *data, int *index)
{
    STACK *node = calloc(1, sizeof(STACK)); /* create new node */

    if (node == NULL){
        (verbose) && fprintf(stderr, "Error: no stack space available for node\n");
        abort();
    }
    else {
        node->index = *index;
        memcpy(node->type, type, 10);
        node->datalen = sizeof(data);
        node->data = data;
        node->next = (*index == 0) ? NULL : *head;  /* insert new head if any */ // segv

        *head = node;
        (verbose) && fprintf(stderr, "push: head: %p, node: %p, next: %p, index: %2d, type: %s, length: %ld, data: %s\n", 
            head, node, node->next, node->index, node->type, node->datalen, node->data);
    }
    (*index)++;
}


int structsize (void)
{
    return sizeof(STACK);
}

/* peek at how big the stacked-list is */
int peek (STACK *head)
{
    return head->index;
}


/* pop off the stack. sets the new head. returns nothing */
void pop(STACK *head)
{
    if (head == NULL) {
        (verbose) && fprintf(stderr, "Error: stack underflow attempt. This will be reported. No soup for you\n");
        abort();
    }
    else {

        (verbose) && fprintf(stderr, "pop: %p:index:%d, type: %s, next: %p, data: %s\n", 
            head, head->index, head->type, head->next, head->data);

        // rename head "top" and next in list becomes "head"
        STACK *top = head;

        // move pointer to new head
        if (head->index != 0)
            *head = *(top)->next;

        // free the popp'ed head
        if (top == NULL)
            free(top);

    }
}

stack.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <err.h>

#include "stacklib.h"

/* externs */
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;
extern int verbose = 0;

/* prototypes */
int structsize (void);
void usage (int rc);
void do_opts (int argc, char *argv[], char ** environ);



/* options */
char *file;
int help;
FILE *fp;

char me[] = "stack";

void do_opts(int argc, char *argv[], char ** environ)
{

    char *mode = { "r" };       /* always read */
    /* options struct */
    static struct option longopts[] = {
        { "file",    required_argument, NULL,    'i' },
        { "help",    no_argument,       &help,    1  },
        { "verbose", no_argument,       &verbose, 1  },
        { NULL,      no_argument,       NULL,     0  },
    };

    int ch;
    while ((ch = getopt_long(argc, argv, "i:vh", longopts, NULL)) != -1)
        switch (ch) {
            case 'i':
                if ( (fp = fopen(optarg, mode)) == NULL) {
                    err(1, "%s", optarg);
                }
                //fprintf(stderr, "Opened %s for reading\n", optarg);

                break;
            case 'v':
                verbose = 1;
                break;
            case 'h':
                usage(0);
                break;
            default:
                usage(0);
        }

    argc -= optind;
    argv += optind;
}

int main(int argc, char *argv[], char ** environ) {

    /* pointer that points to pointer of struct */
    STACK **head = calloc(1, sizeof(STACK)); /* create initial node */

    fp = stdin;
    int i, index = -1;
    char *line;
    void *the_data = NULL;

    /* variable type */
    char *type = "char";
    int typei = 0;

    do_opts(argc, argv, environ);

    for (;;) {
        //printf("Enter some text: ");

        // needed else pointer gets recycled..
        line = malloc(MAXLINE);
        the_data = line;

        if(fgets(line, MAXLINE, fp)) { 
            (verbose) && fprintf(stderr, "Entered: %s\n", line);
            index = (index == -1) ? 0 : index;

            if ( (strcmp(type, "int") == 0) || (strcmp(type, "long") == 0) )
                typei = 1;
            else if (strcmp(type, "float") == 0)
                typei = 2;
            else if (strcmp(type, "char") == 0)
                typei = 3;
            else 
                break;

            (verbose) &&
                fprintf(stderr, "maxline = %d, to push: %p, type: %s, the_data: %s\n", 
                    MAXLINE, *head, type, the_data);
            push(head, type, the_data, &index);

        }
        else 
            break;
    }

    if (index >= 0) {
        (verbose) && fprintf(stderr, "Stack index (0 based): %d\tStruct size is %d bytes\n", 
        peek(*head), structsize());
    }

    if (index >= 0) {
        for (i = index; i > 0; i--) {
            printf("%s", (*head)->data);
            pop(*head);
        }
    }


}

void usage (int rc)
{
    printf("usage: %s [-i <file>][-vh]  input is a file -or- stdin\n", me);
    exit(rc);
}

Compile with:

gcc -Wall -o stack stacklib.c stack.c

run it - and verify output:

% cat /etc/hosts; echo '---->>>>>>>>>>>>  <<<<<<<<<-------'; ./stack -i /etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1   localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0 localhost
---->>>>>>>>>>>>  <<<<<<<<<-------
fe80::1%lo0 localhost
::1             localhost
255.255.255.255 broadcasthost
127.0.0.1   localhost
##
# when the system is booting.  Do not change this entry.
# localhost is used to configure the loopback interface
#
# Host Database
##

Published on in