#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
/* #define DEBUG */
#if defined(DEBUG)
#include "xmalloc.h"
#else
#define xmalloc(x, y) malloc(x)
#define xfree(x, y) free(x)
#define xrealloc(x, y, z) realloc(x, y)
#define xmallocdump()
#endif
#define ID_GETLINE 1001
#define ID_NODE 1002
#define ID_STR 1004
#define ID_WORDDATA 1005
#define BUFFSIZE 3 /* >= 2 */
char *mygetline(FILE *fp) {
static char inbuff[BUFFSIZE];
char *outbuff_malloc, *tmpbuff;
char *p, *r;
int fEOL;
if ((outbuff_malloc = xmalloc(1, ID_GETLINE)) == NULL) {
return NULL;
}
*outbuff_malloc = '\0';
fEOL = 0;
do {
r = fgets(inbuff, BUFFSIZE, fp);
if (r == NULL)
break;
for (p = inbuff; *p != '\0'; p++)
;
if (*(p - 1) == '\n')
fEOL = 1;
if ((tmpbuff = xrealloc(outbuff_malloc, strlen(outbuff_malloc) + strlen(inbuff) + 1, ID_GETLINE)) == NULL) {
xfree(outbuff_malloc, ID_GETLINE);
return NULL;
}
strcat(tmpbuff, inbuff);
outbuff_malloc = tmpbuff;
} while (!fEOL);
if (strlen(outbuff_malloc) > 0) {
for (p = outbuff_malloc; *p != '\0'; p++)
;
if (*(p - 1) == '\n')
*(p - 1) = '\0';
return outbuff_malloc;
}
xfree(outbuff_malloc, ID_GETLINE);
return NULL;
}
struct node {
void *data;
struct node *left;
struct node *right;
};
void *push(struct node **root, void *data, int (*cmp)(void *, void *)) {
struct node *p;
int c;
if (!*root) {
p = xmalloc(sizeof(struct node), ID_NODE);
p->data = data;
p->left = p->right = NULL;
*root = p;
return data;
}
if ((c = (*cmp)(data, (*root)->data)) != 0) {
if (c < 0) {
return push(&((*root)->left), data, cmp);
} else if (c > 0) {
return push(&((*root)->right), data, cmp);
}
}
return (*root)->data;
}
void *pop(struct node **root) {
struct node *p;
void *data;
if (*root == NULL) {
return NULL;
}
if ((*root)->left) {
data = pop(&((*root)->left));
return data;
}
assert((*root)->left == NULL);
p = *root;
data = p->data;
*root = p->right;
xfree(p, ID_NODE);
return data;
}
struct worddata {
char *word;
int n;
};
int isDelimiter(char c) {
return iscntrl(0xff & c) || isdigit(0xff & c) || (isprint(0xff & c) && !isalpha(0xff & c));
}
int cmp(struct worddata *s, struct worddata *t) {
return strcmp(s->word, t->word);
}
void wdregister(char *p, struct node **root) {
char *word;
struct worddata *wd, *already;
word = xmalloc(strlen(p) + 1, ID_STR);
if (!word) {
fprintf(stderr, "cannot allocate enough memory, aborted.\n");
exit(-1);
}
strcpy(word, p);
wd = xmalloc(sizeof(struct worddata), ID_WORDDATA);
if (!wd) {
fprintf(stderr, "cannot allocate enough memory, aborted.\n");
exit(-1);
}
wd->word = word;
wd->n = 1;
if ((already = push(root, wd, (int (*)(void *, void *))cmp)) != wd) {
xfree(wd->word, ID_STR);
xfree(wd, ID_WORDDATA);
already->n++;
}
}
void analyze_line(char *line, struct node **root) {
char *p, *q;
for (p = line; *p;) {
for (;isDelimiter(*p) && *p; p++)
;
if (!*p)
break;
for (q = p; !isDelimiter(*q) && *q; q++)
;
if (*q) {
*q = '\0';
wdregister(p, root);
p = q + 1;
} else {
wdregister(p, root);
break;
}
}
}
int main() {
struct node *root;
struct worddata *p;
char *line;
root = NULL;
while ((line = mygetline(stdin)) != NULL) {
analyze_line(line, &root);
xfree(line, ID_GETLINE);
}
while ((p = pop(&root)) != NULL) {
printf("%s: %d\n", p->word, p->n);
xfree(p->word, ID_STR);
xfree(p, ID_WORDDATA);
}
xmallocdump();
return 0;
}
/* end */