293 lines
5 KiB
Text
293 lines
5 KiB
Text
|
|
%{
|
|
#include <config.h>
|
|
|
|
#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);
|
|
|
|
free(last_label);
|
|
last_label = strdup(new);
|
|
|
|
return new;
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
return yyparse();
|
|
}
|