/* xa65 - 65xx/65816 cross-assembler and utility suite * * Copyright (C) 1989-1997 Andre Fachat (afachat@gmx.de) * Maintained by Cameron Kaiser * * File handling and preprocessor (also see xaa.c) module * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #ifndef _MSC_VER #include #endif #include "xad.h" #include "xah.h" #include "xah2.h" #include "xar.h" #include "xa.h" #include "xam.h" #include "xal.h" #include "xat.h" #include "xap.h" /* define this for recursive evaluation output */ #undef DEBUG_RECMAC #undef DEBUG_REPLACE char s[MAXLINE]; Datei *filep; static int tcompare(char*, char**, int); static int pp_replace(char*, char*, int, int); static int searchdef(char*); static int fgetline(char*, int len, int *rlen, FILE*); /*static int icl_open(char*);*/ static int pp_ifdef(char*), pp_ifndef(char*); static int pp_else(char*), pp_endif(char*); static int pp_echo(char*), pp_if(char*), pp_print(char*), pp_prdef(char*); static int pp_ifldef(char*), pp_iflused(char*); static int pp_undef(char*); static int pp_error(char*); #define ANZBEF 14 #define VALBEF 7 static int quotebs = 0; static int inquote = 0; static char *cmd[] = { "echo", "include", "define", "undef", "printdef", "print", "error", /* VALBEF */ "ifdef", "ifndef", "else", "endif", "ifldef", "iflused", "if" }; /* ANZBEF */ static int (*func[])(char*) = {pp_echo,icl_open,pp_define,pp_undef, pp_prdef,pp_print,pp_error,pp_ifdef,pp_ifndef, pp_else,pp_endif, pp_ifldef,pp_iflused,pp_if }; static char *mem; static unsigned long memfre; static int nlf; static int nff; static int hashindex[256]; static List *liste; static unsigned int rlist; static int fsp; static int loopfl; static Datei flist[MAXFILE + 1]; static char in_line[MAXLINE]; int pp_comand(char *t) { int i, l, er = 1; i = tcompare(t, cmd, ANZBEF); if (i >= 0) { if (loopfl && (i < VALBEF)) er = 0; else { l = (int) strlen(cmd[i]); while (isspace(t[l])) l++; er = (*func[i])(t + l); } } return (er); } int pp_ifdef(char *t) { /* int x; printf("t=%s\n",t); x=searchdef(t); printf("searchdef(t)=%d\n",x); */ loopfl = (loopfl << 1) + (searchdef(t) ? 0 : 1); return (0); } int pp_ifndef(char *t) { loopfl = (loopfl << 1) + (searchdef(t) ? 1 : 0); return (0); } int pp_ifldef(char *t) { loopfl = (loopfl << 1) + (ll_pdef(t) ? 1 : 0); return (0); } int pp_iflused(char *t) { int n; loopfl = (loopfl << 1) + (ll_search(t, &n, STD) ? 1 : 0); return (0); } int pp_echo(char *t) { int er; if ((er = pp_replace(s, t, -1, rlist))) errout(er); else { logout(s); logout("\n"); } return (0); } int pp_print(char *t) { int f, a, er; logout(t); if ((er = pp_replace(s, t, -1, rlist))) { logout("\n"); errout(er); } else { logout("="); logout(s); logout("="); er = b_term(s, &a, &f, pc[segment]); if (er) { logout("\n"); errout(er); } else { sprintf(s, "%d\n", a); logout(s); } } return (0); } int pp_error(char *t) { /* the offending line is printed for us */ return E_UERROR; } int pp_if(char *t) { int a, f, l, er; /* XXX: #if XYZ, if XYZ is not defined, XYZ gets treated as a label. */ if ((er = pp_replace(s, t, -1, rlist))) { errout(er); } else { dsb_len = 1; f = b_term(s, &a, &l, pc[segment]); dsb_len = 0; if ((!loopfl) && f) { errout(f); } else loopfl = (loopfl << 1) + (a ? 0 : 1); } return (0); } int pp_else(char *t) { (void) t; /* quench warning */ loopfl ^= 1; return (0); } int pp_endif(char *t) { (void) t; /* quench warning */ loopfl = loopfl >> 1; return (0); } /* stub for handling CPP directives */ int pp_cpp(char *t) { char name[MAXLINE]; if (sscanf(t, " %d \"%s\"", &filep->fline, name) == 2) { /* massage it into our parameters and drop last quote */ char *u; filep->fline--; if ((u = (char*) strrchr(name, '"'))) *u = '\0'; free(filep->fname); filep->fname = strdup(name); if (!filep->fname) { fprintf(stderr, "Oops, no more memory!\n"); exit(1); } return (0); } else { return (E_SYNTAX); } } /* pp_undef is a great hack to get it working fast... */ int pp_undef(char *t) { int i; if ((i = searchdef(t))) { i += rlist; liste[i].s_len = 0; } return 0; } int pp_prdef(char *t) { char *x; int i, j; if ((i = searchdef(t))) { i += rlist; x = liste[i].search; sprintf(s, "\n%s", x); if (liste[i].p_anz) { sprintf(s + strlen(s), "("); for (j = 0; j < liste[i].p_anz; j++) { x += strlen(x) + 1; sprintf(s + strlen(s), "%s%c", x, (liste[i].p_anz - j - 1) ? ',' : ')'); } } sprintf(s + strlen(s), "=%s\n", liste[i].replace); logout(s); } return (E_OK); } int searchdef(char *t) { int i = 0, j, k, l = 0; while (t[l] != ' ' && t[l] != '\0') l++; if (rlist) { i = hashindex[hashcode(t, l)]; do /*for(i=0;i= ANZDEF || memfre < MAXLINE * 2) return (E_NOMEM); /* printf("define:mem=%04lx\n",mem); getchar(); */ rl = rlist++; liste[rl].search = mem; for (i = 0; (t[i] != ' ') && (t[i] != '\0') && (t[i] != '('); i++) *mem++ = t[i]; *mem++ = '\0'; memfre -= i + 1; liste[rl].s_len = i; liste[rl].p_anz = 0; /* printf("define:%s\nlen1=%d\n",liste[rl].search,liste[rl].s_len); getchar(); */ if (t[i] == '(') { while (t[i] != ')' && t[i] != '\0') { i++; liste[rl].p_anz++; // skip whitespace before parameter name while (isspace(t[i])) i++; // find length for (j = 0; (!isspace(t[i + j])) && t[i + j] != ')' && t[i + j] != ',' && t[i + j] != '\0'; j++) ; if (j < memfre) { strncpy(mem, t + i, j); mem += j; *mem++ = '\0'; memfre -= j + 1; } i += j; // skip trailing whitespace after parameter name while (isspace(t[i])) i++; } if (t[i] == ')') i++; } while (t[i] == ' ') i++; t += i; er = pp_replace(h, t, -1, rlist); t = h; liste[rl].replace = mem; (void) strcpy(mem, t); mem += strlen(t) + 1; #if 0 /* debug */ { char *ss; if(liste[rl].p_anz) { ss=liste[rl].search; printf("define:\n%s(",liste[rl].search); for(j=0;ji=%d, len=%d\n", n, liste[n].search,i, liste[n].s_len); #endif return i == liste[n].s_len; } /** * this is a break out of the original pp_replace code, as this code * was basically duplicated for initial and recursive calls. * * to -> original output buffer for overflow check (only!) * t -> where to write the output * n -> replace macro n in liste[] * sl -> length of liste[n].search (= liste[n].s_len) * recursive -> * l -> * blist -> * */ static int pp_replace_part(char *to, char *t, int n, int sl, int recursive, int *l, int blist) { int er = E_OK; int i, d; char c; // save mem, to restore it when we don't need the pseudo replacements anymore // Note: in a real version, that should probably be a parameter, and not fiddling // with global variables... char *saved_mem = mem; #ifdef DEBUG_RECMAC printf("replace part: n=%d, sl=%d, rec=%d, %s\n", n, sl, recursive, t); #endif // yes, mark replacement string char *rs = liste[n].replace; // does it have parameters? if (liste[n].p_anz) { // yes, we have parameters, so we need to pp_replace them // as well. char fti[MAXLINE], fto[MAXLINE]; // copy replacement into temp buffer (void) strcpy(fti, liste[n].replace); // boundary checks ... if (blist + liste[n].p_anz >= ANZDEF || memfre < MAXLINE * 2) er = E_NOMEM; else { // ... passed // y points to the char behind the input name (i.e. the '(') char *y = t + sl; // x points into the pp definition, with the parameter definition names // following the definition name, separated by \0. Starts here behind the // name - i.e. now points to the name of the first parameter char *x = liste[n].search + sl + 1; // does the input actually have a '(' as parameter marker? if (*y != '(') { // no. Should probably get an own error (E_PARAMETER_EXPECTED) er = E_SYNTAX; } else { // mx now points to next free memory (mem current pointer in text table) char *mx = mem - 1; // walk through the pp parameters // by creating "fake" preprocessor definitions at the end // of the current definition list, i.e. in liste[] after index // rlist. for (i = 0; i < liste[n].p_anz; i++) { char c; int hkfl = 0; // quote flag int klfl = 0; // brackets counter // create new replacement entry liste[blist + i].search = x; liste[blist + i].s_len = (int) strlen(x); liste[blist + i].p_anz = 0; liste[blist + i].replace = mx + 1; // move x over to the next parameter name x += strlen(x) + 1; // points to first char of the parameter name in the input // copy over first char into text memory (note that the position // is already stored in liste[].replace above) c = *(++mx) = *(++y); // copy over the other characters while (c != '\0' && ((hkfl != 0 || klfl != 0) || (c != ',' && c != ')'))) { if (c == '\"') hkfl = hkfl ^ 1; if (!hkfl) { if (c == '(') klfl++; if (c == ')') klfl--; } c = *(++mx) = *(++y); } // zero-terminate stored string *mx = '\0'; // if i is for the last parameter, then check if the // last copied char was ')', otherwise it should be ',' // as separator for the next parameter if (c != ((i == liste[n].p_anz - 1) ? ')' : ',')) { er = E_ANZPAR; // note: this break only exits the innermost loop! break; } } // at this point we have "augmented" the pp definitions // with a list of new definitions for the macro parameters // so that when pp_replace'ing them recursively the macro parameters // will automatically be replaced. // if we ran into an error, report so if (er != E_OK) { return (er); } // let mx point to first free (and not last non-free) byte mx++; // before we use the parameter replacements, we need to // recursively evaluate and replace them as well. #ifdef DEBUG_RECMAC printf("replace (er=%d):\n", er); printf("%s=%s\n",liste[n].search,liste[n].replace); #endif // loop over all arguments for (i = 0; i < liste[n].p_anz; i++) { /* recursively evaluate arguments */ char nfto[MAXLINE]; char nfwas[MAXLINE]; //int j = 0; //int k = 0; // copy over the replacement string into a buffer nfwas (void) strcpy(nfwas, liste[blist + i].replace); if (!er) { // replace the tokens in the parameter, adding possible pseudo params // on top of the liste[] into nfto er = pp_replace(nfto, nfwas, -1, blist + liste[n].p_anz); } #ifdef DEBUG_RECMAC printf("-%s=%s\n",liste[rlist+i].search,liste[rlist+i].replace); printf("SUBB: -%s=%s\n", nfwas, nfto); #endif #if 0 // as long as the strings don't match, loop... while ((k = strcmp(nfto, nfwas))) { // copy original from nfwas back to nfto (void)strcpy(nfto, nfwas); if (!er) { // save-guard our replacement strings in global memory for the // recursive pp_replace call. Note: is cleaned up after return, // so need to restore mem only at the end of this function. mem = mx; // replace tokens er=pp_replace(nfto,nfwas,-1,rlist); } // and copy result into input buffer (void)strcpy(nfwas, nfto); #ifdef DEBUG_RECMAC printf("SUBB2 (%i): -%s=%s\n", k, liste[rlist+i].replace, nfto); #endif if (++j>10) { // we ran into 10 recursions deep - that does not look sound, bail out errout(E_ORECMAC); } } #endif // catch an error situation if (liste[rlist + i].replace == NULL) { errout(E_ANZPAR); return E_ANZPAR; } // copy over the replacement string into free memory (using mx as pointer) (void) strcpy(mx, nfto); // replace the pointer to the (now obsolete) old replacement with the one we just created // Note that due to the nature of the (more or less static) memory allocation, this is not // being freed. Oh well... liste[blist + i].replace = mx; mx += strlen(mx) + 1; #ifdef DEBUG_RECMAC printf("FINAL: -%s=%s\n",liste[rlist+i].search,liste[rlist+i].replace); #endif } if (!er) { // safe-guard our memory allocations mem = mx; // only change (of two) from recursive: rlist is 0 there #ifdef DEBUG_RECMAC printf("replace macro: recursive=%d, blist=%d, -> b=%d\n", recursive, blist, blist+liste[n].p_anz); printf(" from: %s\n", fti); #endif er = pp_replace(fto, fti, recursive ? 0 : blist, blist + liste[n].p_anz); #ifdef DEBUG_RECMAC printf(" to: %s\n", fto); #endif } /* if(flag) printf("sl=%d,",sl);*/ sl = (int) ((long) y + 1L - (long) t); /* if(flag) printf("sl=%d\n",sl);*/ rs = fto; #ifdef DEBUG_RECMAC printf("->%s\n",fto); #endif } } if (er) { mem = saved_mem; return (er); } } // end if(liste[n].p_anz), i.e. if it has parameters d = (int) strlen(rs) - sl; if (strlen(to) + d >= MAXLINE) { mem = saved_mem; return (E_NOMEM); } /* if(d<0) { y=t+sl+d; x=t+sl; while(*y++=*x++); } if(d>0) { for(ll=strlen(t);ll>=sl;ll--) t[ll+d]=t[ll]; } */ if (d) { // d can be positive or negative, so strcpy cannot be used, use memmove instead (void) memmove(t + sl + d, t + sl, strlen(t) - sl + 1); } i = 0; while ((c = rs[i])) { t[i++] = c; } // other change from recursive. there sl is missing from add //*l=(recursive ? 0 : sl) + d;/*=0;*/ *l = sl + d;/*=0;*/ mem = saved_mem; return (er); } /** * copy the input string pointed to by ti into * an output string buffer pointed to by to, replacing all * preprocessor definitions in the process. * * Note: this method is called recursively, with "a" being -1 * when called from the "outside" respectively in a new context * (like macro parameters) * * The "b" parameter denotes the index in the list from which on * pseudo replacement entries are being created for replacement * parameters * */ int pp_replace(char *to, char *ti, int a, int b) { char *t = to; int l, n, sl, er = E_OK; int ld; // length of name/token to analyse /* int flag=!strncmp(ti,"TOUT",4); if(flag) printf("flag=%d\n",flag); */ // t points to to, so copy input to output 1:1 // then below work on the copy in-place (void) strcpy(t, ti); // if there are replacements in the list of replacements if (rlist) { // loop over the whole input while (t[0] != '\0' && t[0] != ';') { // skip over the whitespace // comment handling is NASTY NASTY NASTY // but I currently don't see another way, as comments and colons // can (and do) appear in preprocessor replacements char quotefl = 0; char commentfl = 0; while ((t[0] != 0) && (quotefl || commentfl || ((!isalpha(t[0]) && t[0] != '_')))) { if (t[0] == '\0') { break; /*return(E_OK);*/ } else { if (t[0] == ';' && !quotefl) { commentfl = 1; } if (t[0] == ':' && !quotefl && !ca65 && !masm) { // note that both ca65 and masm allow colons in comments // so in these cases we cannot reset the comment handling here commentfl = 0; } if (quotefl) { // ignore other quotes within a quote if (t[0] == quotefl) { quotefl = 0; } } else if (t[0] == '"' || t[0] == '\'') { quotefl = t[0]; } t++; ti++; } } // determine the length of the name for (l = 0; isalnum(t[l]) || t[l] == '_'; l++) ; // store in ld ld = l; #ifdef DEBUG_RECMAC printf("l=%d,a=%d,b=%d, t=%s\n",l,a, b ,t); #endif // the following if() is executed when being called from an // 'external' context, i.e. not from a recursion if (a < 0) { // when called from an external context, not by recursion // compute hashcode for the name for the search index entry (liste[n]) n = hashindex[hashcode(t, l)]; // loop over all entries in linked list for hash code (think: hash collisions) do// while(1); { // length of name of pp definition sl = liste[n].s_len; #ifdef DEBUG_RECMAC printf("macro entry: name=%s, sl=%d, p_anz=%d\n", liste[n].search, sl, liste[n].p_anz); #endif // does pp definition match what we have found? if (sl && (sl == l) && check_name(t, n)) { er = pp_replace_part(to, t, n, sl, 0, &l, b); if (er != E_OK) { return er; } break; } if (!n) break; // next index in linked list for given hash code n = liste[n].nextindex; } while (1); } else { // called when in recursive call // loop over all the replacement entries from the given b down to 0 // that allows to replace the parameters first (as they were added at // the end of the list) for (n = b - 1; n >= a; n--) { sl = liste[n].s_len; if (sl && (sl == l) && check_name(t, n)) { er = pp_replace_part(to, t, n, sl, 1, &l, b); break; } } } // advance input by length of name ti += ld; // advance output by l t += l; } /* end while(t[0] != 0) */ } /* end if(rlist) */ return (E_OK); } int pp_init(void) { int er; for (er = 0; er < 256; er++) hashindex[er] = 0; fsp = 0; er = 0; mem = malloc(MAXPP); if (!mem) er = E_NOMEM; memfre = MAXPP; rlist = 0; nlf = 1; nff = 1; if (!er) { liste = malloc((long) ANZDEF * sizeof(List)); if (!liste) er = E_NOMEM; if (!er && !xa23) { er = pp_define("XA_MAJOR " progmajor); if (!er) er = pp_define("XA_MINOR " progminor); if (!er) er = pp_define("XA_PATCH " progpatch); } } return (er); } int pp_open(char *name) { FILE *fp; int l; fp = xfopen(name, "r"); l = strlen(name); /* we have to alloc it dynamically to make the name survive another pp_open - it's used in the cross-reference list */ flist[0].fname = malloc(l + 1); if (!flist[0].fname) { fprintf(stderr, "Oops, no more memory!\n"); exit(1); } (void) strncpy(flist[0].fname, name, l + 1); flist[0].fline = 0; flist[0].bdepth = b_depth(); flist[0].filep = fp; flist[0].flinep = NULL; return (fp == NULL); } void pp_close(void) { if (flist[fsp].bdepth != b_depth()) { fprintf(stderr, "Blocks not consistent in file %s: start depth=%d, end depth=%d\n", flist[fsp].fname, flist[fsp].bdepth, b_depth()); } fclose(flist[fsp].filep); } void pp_end(void) { } Datei* pp_getidat(void) { return &flist[fsp]; } int icl_close(int *c) { *c = '\n'; if (!fsp) return (E_EOF); if (flist[fsp].bdepth != b_depth()) { fprintf(stderr, "Blocks not consistent in file %s: start depth=%d, end depth=%d\n", flist[fsp].fname, flist[fsp].bdepth, b_depth()); } fclose(flist[fsp--].filep); nff = 1; return (E_OK); } int icl_open(char *tt) { FILE *fp2; char *namep; int len, j, i = 0; pp_replace(s, tt, -1, rlist); if (fsp >= MAXFILE) return (-1); if (s[i] == '<' || s[i] == '"') i++; for (j = i; s[j]; j++) if (s[j] == '>' || s[j] == '"') s[j] = '\0'; fp2 = xfopen(s + i, "r"); if (!fp2) return (E_FNF); setvbuf(fp2, NULL, _IOFBF, BUFSIZE); fsp++; namep = s + i; len = strlen(namep); /* we have to alloc it dynamically to make the name survive another pp_open - it's used in the cross-reference list */ flist[fsp].fname = malloc(len + 1); if (!flist[fsp].fname) { fprintf(stderr, "Oops, no more memory!\n"); exit(1); } strncpy(flist[fsp].fname, namep, len + 1); flist[fsp].fline = 0; flist[fsp].bdepth = b_depth(); flist[fsp].flinep = NULL; flist[fsp].filep = fp2; nff = 1; return (0); } int pgetline(char *t) { int c, er = E_OK; int rlen, tlen; char *p = 0; char *q = 0; loopfl = 0; /* set if additional fetch needed */ filep = flist + fsp; do { int is_continuation = 0; tlen = 0; // start of current line in in_line[] do { c = fgetline(in_line + tlen, MAXLINE - tlen, &rlen, flist[fsp].filep); //fprintf(stderr, "fgetline -> c=%02x, rlen->%d, t->%s\n", c, rlen, in_line+tlen); /* check for continuation lines */ is_continuation = ((c == '\n') && (rlen > 0) && (in_line[tlen + rlen - 1] == '\\')); if (is_continuation) { // cut off the continuation character rlen--; in_line[tlen + rlen] = 0; } tlen += rlen; } while (is_continuation); if (in_line[0] == '#' || in_line[0] == altppchar) { if (in_line[1] == ' ') { /* cpp comment -- pp_comand doesn't handle this right */ er = pp_cpp(in_line + 1); } else { if ((er = pp_comand(in_line + 1))) { if (er != 1) { logout(in_line); logout("\n"); } } } } else { er = 1; } if (c == EOF) { if (loopfl && fsp) { char bletch[MAXLINE]; sprintf(bletch, "at end of included file %s:\n", flist[fsp].fname); logout(bletch); errout(W_OPENPP); } er = icl_close(&c); } } while (!er || (loopfl && er != E_EOF)); if (loopfl) { errout(E_OPENPP); } if (!er || loopfl) { in_line[0] = '\0'; } er = (er == 1) ? E_OK : er; if (!er) { #ifdef DEBUG_REPLACE // printf("<<<: %s\n", in_line); #endif er = pp_replace(t, in_line, -1, rlist); #ifdef DEBUG_REPLACE printf(">>>: %s\n", t); #endif } if (!er && nff) er = E_NEWFILE; if (!er && nlf) er = E_NEWLINE; nlf = nff = 0; filep = flist + fsp; filep->flinep = in_line; //fprintf(stderr, "pgetline -> er=%d, t=%s\n", er, t); return (er); } /*************************************************************************/ /* smart getc that can skip C comment blocks */ int rgetc(FILE *fp) { int incomment; int c; static int d; static int d_isvalid = 0; static int last = 0; incomment = 0; do { restart: /* we had a look-ahead to see if two-char sequence was received? */ if (d_isvalid) { c = d; d_isvalid = 0; } else { c = getc(fp); } while (c == 13) { c = getc(fp); }; /* remove ^M for unices */ /* a newlinebreaks any quote */ if (c == '\n') { inquote = 0; flist[fsp].fline++; nlf = 1; } /* check for start of comment anyway, to allow for nested comments */ /* only allow this with 2.3 compatibility */ if (!inquote && c == '/' && (xa23 || !incomment)) { d = getc(fp); // C++ double slash comment if (d == '/') { do { c = getc(fp); } while (c != '\n' && c != EOF); if (c == '\n') { flist[fsp].fline++; nlf = 1; } } else if (d == '*') { /* start block comment */ incomment++; /* convene normal processing */ goto restart; } else { d_isvalid = 1; } } /* here we are in a dilemma. If we interpret quotes in comments, * this may break end-of-block comment when singular quotes are * in a comment. If we don't interpret quotes in a comment, * a quoted string with end-of-comment in it may break when * commented. * * Unfortunately GeckOS has quotes in comments, and in an inconsistent way. * * #define E_FILLNAM <-34 / * illegal name (joker "*","?","\"") * / * * ... except if we handle backslash-escaped quotes? No, then this breaks * in apps/lsh/lsh.a65:347 * * / * TODO: interpret "\" escape codes, shell variables * / * * But I guess for 2.4 it is ok to break things like this. */ if (!incomment) { /* not in comment */ /* flag if we're in a quoted string */ /* but only if quote isnot escaped with backslash */ if (c == '"' && last != '\\' && !quotebs) { inquote ^= 1; } /* implement backslashed quotes for 2.4 */ if (!xa23 && c == '\\') quotebs = 1; else quotebs = 0; } else { /* in comment */ /* check for end of comment */ /* note: for xa2.3, incomment only set true if not quoted, and quote not changed in comment */ if ((!inquote || !xa23) && (c == '*')) { if ((d = getc(fp)) != '/') { d_isvalid = 1; } else { incomment--; /* convene normal processing */ goto restart; } } } /* force newline at the end of a file */ if (c == EOF && last != EOF) { last = EOF; c = '\n'; } else { last = c; } } while (incomment && (c != EOF)); return (c - '\t' ? c : ' '); } /** * Note that the line returned is always zero-terminated, * the rlen out parameter is just convenience, so that * a further strlen() can be saved */ int fgetline(char *t, int len, int *rlen, FILE *fp) { static int c, i; i = 0; do { c = rgetc(fp); if (c == EOF || c == '\n') { t[i] = '\0'; break; } t[i] = c; i = (i < len - 1) ? i + 1 : len - 1; } while (1); *rlen = i; return (c); }