static char *rcs = "$Header: read.c,v 1.1 88/01/15 12:17:05 simpson Rel $"; /* $Log: read.c,v $ * Revision 1.1 88/01/15 12:17:05 simpson * initial release * * Revision 0.1 87/12/11 17:02:13 simpson * beta test * */ #include #include #include "profile.h" #define isoctal(d) ('0' <= d && d <= '7') #define ishex(x) (isdigit(x) || ('a' <= x && x <= 'f') || ('A' <= x && x <= 'F')) #define isprime(c) (c == '\'') #define isbackslash(c) (c == '\\') #define iscaret(c) (c == '^') extern char *strcpy(); extern PROFILE_STANZA *profile_stanza_space(); extern PROFILE_MARKER *profile_marker_space(); extern PROFILE_BINDING *profile_binding_space(); extern PROFILE_VALUE *profile_value_space(); static PROFILE_BINDING *get_binding(); static PROFILE_BINDING *get_bindings(); static PROFILE_MARKER *get_marker(); static PROFILE_MARKER *get_markers(); static PROFILE_VALUE *get_value(); static PROFILE_VALUE *get_values(); static char parse_character(); static PROFILE_VALUE *parse_value(); PROFILE_STANZA *profile_read_stanza (f) FILE *f; { PROFILE_STANZA *stanza; stanza = profile_stanza_space(); if (stanza == NULL) return(NULL); stanza->marker = get_markers(f); if (get_open_bindings(f)) stanza->binding = get_bindings(f); else { profile_free_stanza(stanza); return(NULL); } if (get_close_bindings(f)) return(stanza); else { profile_free_stanza(stanza); return(NULL); } } /* Returns the list of markers at the head of a stanza. */ static PROFILE_MARKER *get_markers (f) FILE *f; { PROFILE_MARKER *head, *tail, *m; head = tail = NULL; while (m = get_marker(f)) if (tail) { tail->next = m; m->previous = tail; tail = m; } else head = tail = m; return(head); } /* Returns the next marker from the head of the stanza. */ static PROFILE_MARKER *get_marker (f) FILE *f; { int n; PROFILE_MARKER *m; char scratch[PROFILE_MAX_TEXT+1]; for (;;) if (n = get_name_text(f, scratch)) { if ((m = profile_marker_space(n)) == NULL) return(NULL); strcpy(m->text, scratch); return(m); } else if (get_end_of_line(f)) continue; else return(NULL); } /* Returns the list of bindings in the body of the stanza. */ static PROFILE_BINDING *get_bindings (f) FILE *f; { PROFILE_BINDING *head, *tail, *b; head = tail = NULL; while (b = get_binding(f)) if (tail) { tail->next = b; b->previous = tail; tail = b; } else head = tail = b; return(head); } /* Returns the next binding in the body of the stanza. */ static PROFILE_BINDING *get_binding (f) FILE *f; { int n; PROFILE_BINDING *b; char scratch[PROFILE_MAX_TEXT+1]; for (;;) if (n = get_name_text(f, scratch)) { if ((b = profile_binding_space(n)) == NULL) return(NULL); strcpy(b->name, scratch); break; } else if (get_end_of_line(f)) continue; else return(NULL); b->value = get_values(f); return(b); } /* Returns the list of values following the name of the binding. */ static PROFILE_VALUE *get_values (f) FILE *f; { PROFILE_VALUE *head, *tail, *v; head = tail = NULL; while (v = get_value(f)) if (tail) { tail->next = v; v->previous = tail; tail = v; } else head = tail = v; return(head); } /* Returns the next value in the binding. */ static PROFILE_VALUE *get_value (f) FILE *f; { char text[PROFILE_MAX_TEXT+1]; int n; for (;;) if (n = get_value_text(f, text)) return(parse_value(text, n)); else if (get_end_of_line(f)) return(NULL); else return(NULL); } /* * Reads the text of the next value (if any) in the binding. Returns * the length of the literal text in characters. */ static int get_value_text (f, text) FILE *f; char *text; { register int c; char *s = text; while ((c = getc(f)) != EOF) switch (c) { case '\b': case '\f': case '\r': case '\t': case ' ': /* white space terminates any text gathered so far */ if (s > text) { *s = '\0'; return(s - text); } continue; case '\n': /* newline terminates a binding */ ungetc(c, f); *s = '\0'; return(s - text); case '#': /* gobble up the comment */ while ((c = getc(f)) != EOF && c != '\n') continue; if (c == '\n') ungetc(c, f); *s = '\0'; return(s - text); case '{': case '}': ungetc(c, f); *s = '\0'; return(s - text); case '"': /* string quotes */ ungetc(c, f); if (s > text) { *s = '\0'; return(s - text); } else return(get_string(f, s)); case '\'': /* character quotes */ ungetc(c, f); if (s > text) { *s = '\0'; return(s - text); } else return(get_character(f, s)); case '\\': /* newline escape */ c = getc(f); if (c == '\n') { if (s > text) { *s = '\0'; return(s - text); } continue; /* just like a blank */ } ungetc(c, f); if (s < &text[PROFILE_MAX_TEXT]) *s++ = '\\'; continue; default: if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; continue; } *s = '\0'; return(s - text); } /* Digests the raw value text returning a new value structure. */ static PROFILE_VALUE *parse_value (s, length) char *s; /* literal text */ int length; /* in characters */ { PROFILE_VALUE *v; if (is_integer(s)) { if ((v = profile_value_space(0)) == NULL) return(NULL); v->class = PROFILE_INTEGER; sscanf(s, "%D", &v->value.i); } else if (is_octal(s)) { if ((v = profile_value_space(0)) == NULL) return(NULL); v->class = PROFILE_OCTAL; /* skip the `0o' prefix */ sscanf(s+2, "%O", &v->value.i); } else if (is_hex(s)) { if ((v = profile_value_space(0)) == NULL) return(NULL); v->class = PROFILE_HEX; /* skip the `0x' prefix */ sscanf(s+2, "%X", &v->value.i); } else if (is_string(s)) { /* be careful when dealing with the empty string "" */ if ((v = profile_value_space(length > 2 ? length - 2 : 1)) == NULL) return(NULL); v->class = PROFILE_STRING; /* erase the terminating double quote */ s[length - 1] = '\0'; /* skip past the initial quote */ parse_string(s + 1, v->value.s); } else if (is_character(s)) { if ((v = profile_value_space(0)) == NULL) return(NULL); v->class = PROFILE_CHARACTER; /* erase the end single quote */ s[length - 1] = '\0'; v->value.c = parse_character(s + 1); } else if (is_float(s)) { if ((v = profile_value_space(0)) == NULL) return(NULL); v->class = PROFILE_FLOAT; sscanf(s, "%E", &v->value.f); } else { if ((v = profile_value_space(length)) == NULL) return(NULL); v->class = PROFILE_OTHER; strcpy(v->value.s, s); } return(v); } /* Converts a string literal to the internal representation. */ static parse_string (source, result) char *source; char *result; { for (; *source; source++) if (*source == '\\') switch (*++source) { case 'b': /* backspace */ *result++ = '\b'; continue; case 'f': /* formfeed */ *result++ = '\f'; continue; case 'n': /* newline */ *result++ = '\n'; continue; case 'r': /* carriage return */ *result++ = '\r'; continue; case 't': /* horizontal tab */ *result++ = '\t'; continue; case '\'': /* single quote */ *result++ = '\''; continue; case '"': /* double quote */ *result++ = '"'; continue; case '\\': /* backslash */ *result++ = '\\'; continue; case '^': /* caret */ *result++ = '^'; continue; case '0': case '1': /* octal constant */ case '2': case '3': case '4': case '5': case '6': case '7': source += parse_octal(source, result) - 2; result++; continue; default: *result++ = *source; /* ignore backslash */ } else if (*source == '^') { /* control escape */ char c = *++source; *result++ = ('@' <= c && c <= '_') ? c - '@' : (c == '?') ? '\177' : c; continue; } else *result++ = *source; *result = '\0'; } /* Converts a character literal to the internal representation. */ static char parse_character (source) char *source; { char c; if (*source == '\\') switch (*++source) { case 'b': /* backspace */ return('\b'); case 'f': /* formfeed */ return('\f'); case 'n': /* newline */ return('\n'); case 'r': /* carriage return */ return('\r'); case 't': /* horizontal tab */ return('\t'); case '\'': /* single quote */ return('\''); case '\\': /* backslash */ return('\\'); case '^': return('^'); case '0': case '1': /* octal constant */ case '2': case '3': case '4': case '5': case '6': case '7': parse_octal(source, &c); return(c); default: return(*source); /* ignore backslash */ } else if (*source == '^') { /* control escape */ c = *++source; return(('@' <= c && c <= '_') ? c - '@' : (c == '?') ? '\177' : c); } else return(*source); } /* Converts an octal escape `\ddd' to its byte representation. */ static int parse_octal (source, result) char *source; char *result; { int count; char byte = '\0'; char digit; for (count = 1; count <= 3; count++) { digit = *source++; if ('0' <= digit && digit <= '7') byte = (byte * 8) + (digit - '0'); else break; } *result = byte; return(count); } /* * Reads the literal text for markers and binding names. Returns the * length in characters of the literal text. */ static int get_name_text (f, text) FILE *f; char *text; { register int c; char *s = text; while ((c = getc(f)) != EOF) switch (c) { case '\b': case '\f': case '\r': case '\t': case ' ': /* white space terminates text gathered so far */ if (s > text) { *s = '\0'; return(s - text); } continue; case '\n': ungetc(c, f); *s = '\0'; return(s - text); case '#': /* gobble up the comment */ while ((c = getc(f)) != EOF && c != '\n') continue; if (c == '\n') ungetc(c, f); *s = '\0'; return(s - text); case '{': case '}': ungetc(c, f); *s = '\0'; return(s - text); case '[': /* sets may contain embedded white space */ if (s + 1 < &text[PROFILE_MAX_TEXT]) { *s++ = '['; if ((c = getc(f)) != EOF) *s++ = c; } while ((c = getc(f)) != EOF) { if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; if (c == ']') break; } continue; case '\\': c = getc(f); if (c == '\n') { if (s > text) { *s = '\0'; return(s - text); } continue; /* just like a blank */ } ungetc(c, f); if (s < &text[PROFILE_MAX_TEXT]) *s++ = '\\'; continue; default: if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; continue; } *s = '\0'; return(s - text); } /* Returns non-zero on end of line and zero otherwise. */ static int get_end_of_line (f) FILE *f; { int c; if ((c = getc(f)) == '\n') return(1); ungetc(c, f); return(0); } /* Returns non-zero on seeing `{' and zero otherwise. */ static int get_open_bindings (f) FILE *f; { int c; if ((c = getc(f)) == '{') return(1); ungetc(c, f); return(0); } /* Returns non-zero on seeing `}' and zero otherwise. */ static int get_close_bindings (f) FILE *f; { int c; if ((c = getc(f)) == '}') return(1); ungetc(c, f); return(0); } /* Reads a string literal returning the length of the literal text in characters */ static int get_string (f, text) FILE *f; char *text; { register int c; char *s = text; /* the first double quote is guaranteed */ *s++ = getc(f); while ((c = getc(f)) != EOF) switch (c) { case '\\': if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; c = getc(f); if (c == EOF) return(s - text); else if (c == '\n') { ungetc(c, f); return(s - text); } else if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; continue; case '"': if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; *s = '\0'; return(s - text); case '\n': ungetc(c, f); *s = '\0'; return(s - text); default: if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; continue; } *s = '\0'; return(s - text); } /* Reads a character literal returning the length of the literal text in characters. */ static int get_character (f, text) FILE *f; char *text; { register int c; char *s = text; /* the first single quote is guaranteed */ *s++ = getc(f); while ((c = getc(f)) != EOF) switch (c) { case '\\': if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; c = getc(f); if (c == EOF) return(s - text); else if (c == '\n') { ungetc(c, f); return(s - text); } else if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; continue; case '\'': if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; *s = '\0'; return(s - text); case '\n': ungetc(c, f); *s = '\0'; return(s - text); default: if (s < &text[PROFILE_MAX_TEXT]) *s++ = c; continue; } *s = '\0'; return(s - text); } /* all regular expressions below are in lex notation */ /* returns non-zero iff -?[0-9]+ matches */ static int is_integer (s) char *s; { char *x; /* -? */ if (*s == '-') s++; /* [0-9]+ */ for (x = s; isdigit(*s); s++) continue; return(s > x && !*s); } /* returns non-zero iff 0[oO][0-7]+ matches */ static int is_octal (s) char *s; { char *x; /* 0 */ if (*s == '0') s++; else return(0); /* [oO] */ if (*s == 'o' || *s == 'O') s++; else return(0); /* [0-7]+ */ for (x = s; isoctal(*s); s++) continue; return(s > x && !*s); } /* returns non-zero iff 0[xX][0-9a-fA-F]+ matches */ static int is_hex (s) char *s; { char *x; /* 0 */ if (*s == '0') s++; else return(0); /* [xX] */ if (*s == 'x' || *s == 'X') s++; else return(0); /* [0-9a-fA-F]+ */ for (x = s; ishex(*s); s++) continue; return(s > x && !*s); } /* returns non-zero iff [eE][-+]?[0-9]+ matches */ static int is_exponent (s) char *s; { char *x; /* [eE] */ if (*s == 'e' || *s == 'E') s++; else return(0); /* [-+]? */ if (*s == '-' || *s == '+') s++; /* [0-9]+ */ for (x = s; isdigit(*s); s++) continue; return(s > x && !*s); } static int is_float (s) char *s; { return(is_integer_part_float(s) || is_fractional_part_float(s) || is_power_float(s)); } /* returns non-zero iff -?[0-9]+"."[0-9]*({exponent})? matches */ static int is_integer_part_float (s) char *s; { char *x; /* -? */ if (*s == '-') s++; /* [0-9]+"." */ for (x = s; isdigit(*s); s++) continue; if (x == s || *s != '.') return(0); /* [0-9]* */ for (s++; isdigit(*s); s++) continue; /* ({exponent})? */ return(*s ? is_exponent(s) : 1); } /* returns non-zero iff -?"."[0-9]+({exponent})? matches */ static int is_fractional_part_float (s) char *s; { char *x; /* -? */ if (*s == '-') s++; /* "." */ if (*s == '.') s++; else return(0); /* [0-9]+({exponent})? */ for (x = s; isdigit(*s); s++) continue; return(s > x ? !*s || is_exponent(s) : 0); } /* returns non-zero iff -?[0-9]+{exponent} matches */ static int is_power_float (s) char *s; { char *x; /* -? */ if (*s == '-') s++; /* [0-9]+{exponent} */ for (x = s; isdigit(*s); s++) continue; return(s > x ? is_exponent(s) : 0); } /* returns non-zero iff '[^^\]' | '\^.' | '\\\\' | '\\'' | '\[0-7]{1-3}' matches */ static int is_character (s) char *s; { char *x; if (isprime(*s)) s++; else return(0); if (isbackslash(*s)) { s++; if ((isbackslash(s[0]) || isprime(s[0]) || !isdigit(s[0])) && isprime(s[1]) && !s[2]) return(1); for (x = s; isoctal(*s); s++) continue; return(x < s && s < (x+4) && isprime(s[0]) && !s[1]); } else if (iscaret(*s)) s++; return(isprint(s[0]) && isprime(s[1]) && !s[2]); } /* returns non-zero iff s is a string constant */ static int is_string (s) char *s; { char *x; if (*s != '"') return(0); for (s++; *s; s++) { if (*s == '"') return(!*++s); /* quote must be followed by null */ if (isbackslash(*s) || iscaret(*s)) { if (*++s) continue; /* legal escape */ return(0); /* null follows \ or ^ */ } } return(0); } /* * read an entire profile, making a bidirectional * circularly linked list * returns pointer to the first stanza or NULL on error */ PROFILE_STANZA *profile_read_profile(f) FILE *f; { PROFILE_STANZA *head = NULL; PROFILE_STANZA *tail = NULL; PROFILE_STANZA *x = NULL; while ((x = profile_read_stanza(f)) != NULL) { if (head == NULL) head = tail = x; else { tail->next = x; x->previous = tail; tail = x; } } if (head != NULL) { tail->next = head; head->previous = tail; } return(head); }