%{
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 1000
#define INDENT_STRING " "
#define PAPER_WIDTH 74
int indent=0;
int line=1;
char *last_label=NULL;
extern int yylex(void);
extern char *yytext;
extern void yyerror(const char *x);
extern char *get_label(const char *label);
extern void set_label(const char *label, const char *target);
char *new_counter(const char *key);
%}
%union {
int def;
char *string;
}
%token NEW_COUNTER LABEL HASH CHAR NEWLINE NO_INDENT RIGHT
%type <string> stuff text
%start doc
%%
doc:
| doc NEWLINE {
printf("\n");
++line;
}
| doc stuff NEWLINE {
if (strlen($2) > (PAPER_WIDTH-(indent ? strlen(INDENT_STRING):0))) {
yyerror("line too long");
}
printf("%s%s\n", indent ? INDENT_STRING:"", $2);
free($2);
indent = 1;
++line;
}
| doc stuff RIGHT stuff NEWLINE {
char fixed[PAPER_WIDTH+1];
int len;
len = PAPER_WIDTH-(strlen($2)+strlen($4));
if (len >= 0) {
memset(fixed, ' ', len);
fixed[len] = '\0';
} else {
yyerror("line too wide");
fixed[0] = '\0';
}
printf("%s%s%s\n", $2, fixed, $4);
free($2);
free($4);
indent = 1;
++line;
}
| doc stuff RIGHT stuff RIGHT stuff NEWLINE {
char fixed[PAPER_WIDTH+1];
int len, l;
len = PAPER_WIDTH-(strlen($2)+strlen($4));
if (len < 0) {
len = 0;
yyerror("line too wide");
}
l = len/2;
memset(fixed, ' ', l);
fixed[l] = '\0';
printf("%s%s%s", $2, fixed, $4);
free($2);
free($4);
l = (len+1)/2;
memset(fixed, ' ', l);
fixed[l] = '\0';
printf("%s%s\n", fixed, $6);
free($6);
indent = 1;
++line;
}
| doc stuff RIGHT stuff RIGHT stuff NEWLINE {
char fixed[PAPER_WIDTH+1];
int len, l;
len = PAPER_WIDTH-(strlen($2)+strlen($4));
if (len < 0) {
len = 0;
yyerror("line too wide");
}
l = len/2;
memset(fixed, ' ', l);
fixed[l] = '\0';
printf("%s%s%s", $2, fixed, $4);
free($2);
free($4);
l = (len+1)/2;
memset(fixed, ' ', l);
fixed[l] = '\0';
printf("%s%s\n", fixed, $6);
free($6);
indent = 1;
++line;
}
;
stuff: {
$$ = strdup("");
}
| stuff text {
$$ = malloc(strlen($1)+strlen($2)+1);
sprintf($$,"%s%s", $1, $2);
free($1);
free($2);
}
;
text: CHAR {
$$ = strdup(yytext);
}
| text CHAR {
$$ = malloc(strlen($1)+2);
sprintf($$,"%s%s", $1, yytext);
free($1);
}
| NO_INDENT {
$$ = strdup("");
indent = 0;
}
| HASH {
$$ = strdup("#");
}
| LABEL {
if (($$ = get_label(yytext)) == NULL) {
set_label(yytext, last_label);
$$ = strdup("");
}
}
| NEW_COUNTER {
$$ = new_counter(yytext);
}
;
%%
typedef struct node_s {
struct node_s *left, *right;
const char *key;
char *value;
} *node_t;
node_t label_root = NULL;
node_t counter_root = NULL;
static const char *find_key(node_t root, const char *key)
{
while (root) {
int cmp = strcmp(key, root->key);
if (cmp > 0) {
root = root->right;
} else if (cmp) {
root = root->left;
} else {
return root->value;
}
}
return NULL;
}
static node_t set_key(node_t root, const char *key, const char *value)
{
if (root) {
int cmp = strcmp(key, root->key);
if (cmp > 0) {
root->right = set_key(root->right, key, value);
} else if (cmp) {
root->left = set_key(root->left, key, value);
} else {
free(root->value);
root->value = strdup(value);
}
} else {
root = malloc(sizeof(struct node_s));
root->right = root->left = NULL;
root->key = strdup(key);
root->value = strdup(value);
}
return root;
}
void yyerror(const char *x)
{
fprintf(stderr, "line %d: %s\n", line, x);
}
char *get_label(const char *label)
{
const char *found = find_key(label_root, label);
if (found) {
return strdup(found);
}
return NULL;
}
void set_label(const char *label, const char *target)
{
if (target == NULL) {
yyerror("no hanging value for label");
target = "<??" ">"; /* avoid trigraph warning */
}
label_root = set_key(label_root, label, target);
}
char *new_counter(const char *key)
{
int i=0, j, ndollars = 0;
const char *old;
char *new;
if (key[i++] != '#') {
yyerror("bad index");
return strdup("<???" ">"); /* avoid trigraph warning */
}
while (key[i] == '$') {
++ndollars;
++i;
}
key += i;
old = find_key(counter_root, key);
new = malloc(20*ndollars);
if (old) {
for (j=0; ndollars > 1 && old[j]; ) {
if (old[j++] == '.' && --ndollars <= 0) {
break;
}
}
if (j) {
strncpy(new, old, j);
}
if (old[j]) {
i = atoi(old+j);
} else {
new[j++] = '.';
i = 0;
}
} else {
j=0;
while (--ndollars > 0) {
new[j++] = '0';
new[j++] = '.';
}
i = 0;
}
new[j] = '\0';
sprintf(new+j, "%d", ++i);
counter_root = set_key(counter_root, key, new);
if (last_label) {
free(last_label);
}
last_label = strdup(new);
return new;
}
int
main(void)
{
return yyparse();
}