Beispiel #1
0
BOOL wait_for_call(COM_HANDLE com_handle)
{
	char		str[128];
	char*		p;
	BOOL		result=TRUE;
	DWORD		events=0;
	time_t		start=time(NULL);

	ZERO_VAR(cid_name);
	ZERO_VAR(cid_number);

	if(!comRaiseDTR(com_handle))
		lprintf(LOG_ERR,"ERROR %u raising DTR", COM_ERROR_VALUE);

	if(com_alreadyconnected)
		return TRUE;

	if(!mdm_null) {
		if(mdm_init[0]) {
			lprintf(LOG_INFO,"Initializing modem:");
			if(!modem_command(com_handle, mdm_init))
				return FALSE;
		}
		if(!mdm_manswer && mdm_autoans[0]) {
			lprintf(LOG_INFO,"Setting modem to auto-answer:");
			if(!modem_command(com_handle, mdm_autoans))
				return FALSE;
		}
		if(mdm_cid[0]) {
			lprintf(LOG_INFO,"Enabling modem Caller-ID:");
			if(!modem_command(com_handle, mdm_cid))
				return FALSE;
		}
	}

	lprintf(LOG_INFO,"Waiting for incoming call (%s) ...", mdm_manswer ? "Ring Indication" : "Carrier Detect");
	while(1) {
		if(terminated)
			return FALSE;
		if(comReadLine(com_handle, str, sizeof(str), /* timeout (ms): */250) > 0) {
			truncsp(str);
			if(str[0]==0)
				continue;
			lprintf(LOG_DEBUG,"Received from modem: '%s'", str);
			p=str;
			SKIP_WHITESPACE(p);
			if(*p) {
				lprintf(LOG_INFO, "Modem Message: %s", p);
				if(strncmp(p,"CONNECT ",8)==0) {
					long rate=atoi(p+8);
					if(rate)
						SAFEPRINTF2(termspeed,"%u,%u", rate, rate);
				}
				else if(strncmp(p,"NMBR",4)==0 || strncmp(p,"MESG",4)==0) {
					p+=4;
					FIND_CHAR(p,'=');
					SKIP_CHAR(p,'=');
					SKIP_WHITESPACE(p);
					if(cid_number[0]==0)	/* Don't overwrite, if multiple messages received */
						SAFECOPY(cid_number, p);
				}
				else if(strncmp(p,"NAME",4)==0) {
					p+=4;
					FIND_CHAR(p,'=');
					SKIP_CHAR(p,'=');
					SKIP_WHITESPACE(p);
					SAFECOPY(cid_name, p);
				}
				else if(strcmp(p,"NO CARRIER")==0) {
					ZERO_VAR(cid_name);
					ZERO_VAR(cid_number);
				}
				else if(mdm_ring[0] && strcmp(p,mdm_ring)==0 && mdm_manswer && mdm_answer[0]) {
					if(!modem_send(com_handle, mdm_answer)) {
						lprintf(LOG_ERR,"ERROR %u sending modem command (%s) on %s"
							,COM_ERROR_VALUE, mdm_answer, com_dev);
					}
				}
			}
			continue;	/* don't check DCD until we've received all the modem msgs */
		}
		if(carrier_detect(com_handle))
			break;
		if(mdm_reinit && (time(NULL)-start)/60 >= mdm_reinit) {
			lprintf(LOG_INFO,"Re-initialization timer elapsed: %u minutes", mdm_reinit);
			return TRUE;
		}
	}

	if(strcmp(cid_name,"P")==0)
		SAFECOPY(cid_name,"Private");
	else if(strcmp(cid_name,"O")==0)
		SAFECOPY(cid_name,"Out-of-area");

	if(strcmp(cid_number,"P")==0)
		SAFECOPY(cid_number,"Private");
	else if(strcmp(cid_number,"O")==0)
		SAFECOPY(cid_number,"Out-of-area");

	lprintf(LOG_INFO,"Carrier detected");
	return TRUE;
}
Beispiel #2
0
int main(int argc, char **argv)
{
	const char *b, *e, *p;
	const char *output_file = NULL;
	int f, tot, last, linenum, err, parse_err;
	struct timer *t = NULL, *t2;
	struct eb32_node *n;
	int val, test;
	int array[5];
	int filter_acc_delay = 0, filter_acc_count = 0;
	int filter_time_resp = 0;
	int skip_fields = 1;

	argc--; argv++;
	while (argc > 0) {
		if (*argv[0] != '-')
			break;

		if (strcmp(argv[0], "-ad") == 0) {
			if (argc < 2) die("missing option for -ad");
			argc--; argv++;
			filter |= FILT_ACC_DELAY;
			filter_acc_delay = atol(*argv);
		}
		else if (strcmp(argv[0], "-ac") == 0) {
			if (argc < 2) die("missing option for -ac");
			argc--; argv++;
			filter |= FILT_ACC_COUNT;
			filter_acc_count = atol(*argv);
		}
		else if (strcmp(argv[0], "-rt") == 0) {
			if (argc < 2) die("missing option for -rt");
			argc--; argv++;
			filter |= FILT_TIME_RESP;
			filter_time_resp = atol(*argv);
		}
		else if (strcmp(argv[0], "-RT") == 0) {
			if (argc < 2) die("missing option for -RT");
			argc--; argv++;
			filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP;
			filter_time_resp = atol(*argv);
		}
		else if (strcmp(argv[0], "-s") == 0) {
			if (argc < 2) die("missing option for -s");
			argc--; argv++;
			skip_fields = atol(*argv);
		}
		else if (strcmp(argv[0], "-e") == 0)
			filter |= FILT_ERRORS_ONLY;
		else if (strcmp(argv[0], "-E") == 0)
			filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS;
		else if (strcmp(argv[0], "-c") == 0)
			filter |= FILT_COUNT_ONLY;
		else if (strcmp(argv[0], "-q") == 0)
			filter |= FILT_QUIET;
		else if (strcmp(argv[0], "-v") == 0)
			filter_invert = !filter_invert;
		else if (strcmp(argv[0], "-gt") == 0)
			filter |= FILT_GRAPH_TIMERS;
		else if (strcmp(argv[0], "-pct") == 0)
			filter |= FILT_PERCENTILE;
		else if (strcmp(argv[0], "-o") == 0) {
			if (output_file)
				die("Fatal: output file name already specified.\n");
			if (argc < 2)
				die("Fatal: missing output file name.\n");
			output_file = argv[1];
		}
		argc--;
		argv++;
	}

	if (!filter)
		die("No action specified.\n");

	if (filter & FILT_ACC_COUNT && !filter_acc_count)
		filter_acc_count=1;

	if (filter & FILT_ACC_DELAY && !filter_acc_delay)
		filter_acc_delay = 1;

	linenum = 0;
	tot = 0;
	parse_err = 0;

	while ((line = fgets2(stdin)) != NULL) {
		linenum++;

		test = 1;
		if (filter & FILT_TIME_RESP) {
			int tps;

			/* only report lines with response times larger than filter_time_resp */
			b = field_start(line, TIME_FIELD + skip_fields);
			if (!*b) {
				truncated_line(linenum, line);
				continue;
			}

			e = field_stop(b + 1);
			/* we have field TIME_FIELD in [b]..[e-1] */

			p = b;
			err = 0;
			for (f = 0; f < 4 && *p; f++) {
				tps = str2ic(p);
				if (tps < 0) {
					tps = -1;
					err = 1;
				}

				SKIP_CHAR(p, '/');
			}

			if (f < 4) {
				parse_err++;
				continue;
			}

			test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP);
		}

		if (filter & FILT_ERRORS_ONLY) {
			/* only report erroneous status codes */
			b = field_start(line, STATUS_FIELD + skip_fields);
			if (!*b) {
				truncated_line(linenum, line);
				continue;
			}
			if (*b == '-') {
				test &= !!(filter & FILT_INVERT_ERRORS);
			} else {
				val = strl2ui(b, 3);
				test &= (val >= 500 && val <= 599) ^ !!(filter & FILT_INVERT_ERRORS);
			}
		}

		if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
			b = field_start(line, ACCEPT_FIELD + skip_fields);
			if (!*b) {
				truncated_line(linenum, line);
				continue;
			}

			tot++;
			val = convert_date(b);
			//printf("date=%s => %d\n", b, val);
			if (val < 0) {
				parse_err++;
				continue;
			}

			t2 = insert_value(&timers[0], &t, val);
			t2->count++;
			continue;
		}

		if (filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE)) {
			int f;

			b = field_start(line, TIME_FIELD + skip_fields);
			if (!*b) {
				truncated_line(linenum, line);
				continue;
			}

			e = field_stop(b + 1);
			/* we have field TIME_FIELD in [b]..[e-1] */

			p = b;
			err = 0;
			for (f = 0; f < 5 && *p; f++) {
				array[f] = str2ic(p);
				if (array[f] < 0) {
					array[f] = -1;
					err = 1;
				}

				SKIP_CHAR(p, '/');
			}

			if (f < 5) {
				parse_err++;
				continue;
			}

			/* if we find at least one negative time, we count one error
			 * with a time equal to the total session time. This will
			 * emphasize quantum timing effects associated to known
			 * timeouts. Note that on some buggy machines, it is possible
			 * that the total time is negative, hence the reason to reset
			 * it.
			 */

			if (filter & FILT_GRAPH_TIMERS) {
				if (err) {
					if (array[4] < 0)
						array[4] = -1;
					t2 = insert_timer(&timers[0], &t, array[4]);  // total time
					t2->count++;
				} else {
					int v;

					t2 = insert_timer(&timers[1], &t, array[0]); t2->count++;  // req
					t2 = insert_timer(&timers[2], &t, array[2]); t2->count++;  // conn
					t2 = insert_timer(&timers[3], &t, array[3]); t2->count++;  // resp

					v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
					if (v < 0 && !(filter & FILT_QUIET))
						fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
							line, array[0], array[1], array[2], array[3], array[4], v);
					t2 = insert_timer(&timers[4], &t, v); t2->count++;
					tot++;
				}
			} else { /* percentile */
				if (err) {
					if (array[4] < 0)
						array[4] = -1;
					t2 = insert_value(&timers[0], &t, array[4]);  // total time
					t2->count++;
				} else {
					int v;

					t2 = insert_value(&timers[1], &t, array[0]); t2->count++;  // req
					t2 = insert_value(&timers[2], &t, array[2]); t2->count++;  // conn
					t2 = insert_value(&timers[3], &t, array[3]); t2->count++;  // resp

					v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
					if (v < 0 && !(filter & FILT_QUIET))
						fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
							line, array[0], array[1], array[2], array[3], array[4], v);
					t2 = insert_value(&timers[4], &t, v); t2->count++;
					tot++;
				}
			}
			continue;
		}

		test ^= filter_invert;
		if (!test)
			continue;

		/* all other cases mean we just want to count lines */
		tot++;
		if (!(filter & FILT_COUNT_ONLY))
			puts(line);
	}

	if (t)
		free(t);

	if (filter & FILT_COUNT_ONLY) {
		printf("%d\n", tot);
		exit(0);
	}

	if (filter & FILT_ERRORS_ONLY)
		exit(0);

	if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
		/* sort and count all timers. Output will look like this :
		 * <accept_date> <delta_ms from previous one> <nb entries>
		 */
		n = eb32_first(&timers[0]);

		if (n)
			last = n->key;
		while (n) {
			unsigned int d, h, m, s, ms;

			t = container_of(n, struct timer, node);
			h = n->key;
			d = h - last;
			last = h;

			if (d >= filter_acc_delay && t->count >= filter_acc_count) {
				ms = h % 1000; h = h / 1000;
				s = h % 60; h = h / 60;
				m = h % 60; h = h / 60;
				tot++;
				printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count);
			}
			n = eb32_next(n);
		}
	}
Beispiel #3
0
/* Important change as of Nov-16-2006, 'cmdline' may contain args */
long sbbs_t::exec_bin(const char *cmdline, csi_t *csi, const char* startup_dir)
{
    char    str[MAX_PATH+1];
	char	mod[MAX_PATH+1];
	char	modname[MAX_PATH+1];
	char*	p;
	int 	file;
    csi_t   bin;

	SAFECOPY(mod,cmdline);
	p=mod;
	FIND_CHAR(p,' ');
	if(*p) {
		*p=0;				/* terminate 'mod' */
		p++;				/* skip space */
		SKIP_CHAR(p,' ');	/* skip more spaces */
	}
	if(*p)
		strcpy(main_csi.str, p);

#ifdef JAVASCRIPT
	if((p=getfext(mod))!=NULL && stricmp(p,".js")==0)
		return(js_execfile(cmdline, startup_dir));
	if(p==NULL && startup_dir!=NULL && *startup_dir) {
		SAFEPRINTF2(str,"%s%s.js", startup_dir, mod);
		if(fexistcase(str))
			return(js_execfile(cmdline, startup_dir));
	}
	if(cfg.mods_dir[0]) {
		SAFEPRINTF2(str,"%s%s.js",cfg.mods_dir,mod);
		if(fexistcase(str)) 
			return(js_execfile(cmdline, startup_dir));
	}
#endif

	SAFECOPY(modname,mod);
	if(!strchr(modname,'.'))
		strcat(modname,".bin");

	SAFEPRINTF2(str,"%s%s",cfg.mods_dir,modname);
	if(cfg.mods_dir[0]==0 || !fexistcase(str)) {

#ifdef JAVASCRIPT
		SAFEPRINTF2(str,"%s%s.js",cfg.exec_dir,mod);
		if(fexistcase(str)) 
			return(js_execfile(cmdline, startup_dir));
#endif

		SAFEPRINTF2(str,"%s%s",cfg.exec_dir,modname);
		fexistcase(str);
	}
	if((file=nopen(str,O_RDONLY))==-1) {
		errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
		return(-1); 
	}

	memcpy(&bin,csi,sizeof(csi_t));
	clearvars(&bin);
	bin.length=(uint32_t)filelength(file);
	if((bin.cs=(uchar *)malloc(bin.length))==NULL) {
		close(file);
		errormsg(WHERE,ERR_ALLOC,str,bin.length);
		return(-1); 
	}
	if(lread(file,bin.cs,bin.length)!=bin.length) {
		close(file);
		errormsg(WHERE,ERR_READ,str,bin.length);
		free(bin.cs);
		return(-1); 
	}
	close(file);

	bin.ip=bin.cs;
	bin.rets=0;
	bin.cmdrets=0;
	bin.misc=0;

	while(exec(&bin)==0)
		if(!(bin.misc&CS_OFFLINE_EXEC)) {
			checkline();
			if(!online)
				break; 
		}

	freevars(&bin);
	free(bin.cs);
	csi->logic=bin.logic;
	return(bin.retval);
}
Beispiel #4
0
/*!
  Load, parse, and process a qdoc configuration file. This
  function is only called by the other load() function, but
  this one is recursive, i.e., it calls itself when it sees
  an \c{include} statement in the qdog configuration file.
 */
void Config::load(Location location, const QString& fileName)
{
    QRegExp keySyntax("\\w+(?:\\.\\w+)*");

#define SKIP_CHAR() \
    do { \
        location.advance(c); \
        ++i; \
        c = text.at(i); \
        cc = c.unicode(); \
    } while (0)

#define SKIP_SPACES() \
    while (c.isSpace() && cc != '\n') \
        SKIP_CHAR()

#define PUT_CHAR() \
    word += c; \
    SKIP_CHAR();

    if (location.depth() > 16)
        location.fatal(tr("Too many nested includes"));

    QFile fin(fileName);
    if (!fin.open(QFile::ReadOnly | QFile::Text)) {
        fin.setFileName(fileName + ".qdoc");
        if (!fin.open(QFile::ReadOnly | QFile::Text))
            location.fatal(tr("Cannot open file '%1': %2").arg(fileName).arg(fin.errorString()));
    }

    QString text = fin.readAll();
    text += QLatin1String("\n\n");
    text += QChar('\0');
    fin.close();

    location.push(fileName);
    location.start();

    int i = 0;
    QChar c = text.at(0);
    uint cc = c.unicode();
    while (i < (int) text.length()) {
        if (cc == 0)
            ++i;
        else if (c.isSpace()) {
            SKIP_CHAR();
        }
        else if (cc == '#') {
            do {
                SKIP_CHAR();
            } while (cc != '\n');
        }
        else if (isMetaKeyChar(c)) {
            Location keyLoc = location;
            bool plus = false;
            QString stringValue;
            QStringList stringListValue;
            QString word;
            bool inQuote = false;
            bool prevWordQuoted = true;
            bool metWord = false;

            MetaStack stack;
            do {
                stack.process(c, location);
                SKIP_CHAR();
            } while (isMetaKeyChar(c));

            QStringList keys = stack.getExpanded(location);
            SKIP_SPACES();

            if (keys.count() == 1 && keys.first() == "include") {
                QString includeFile;

                if (cc != '(')
                    location.fatal(tr("Bad include syntax"));
                SKIP_CHAR();
                SKIP_SPACES();
                while (!c.isSpace() && cc != '#' && cc != ')') {
                    includeFile += c;
                    SKIP_CHAR();
                }
                SKIP_SPACES();
                if (cc != ')')
                    location.fatal(tr("Bad include syntax"));
                SKIP_CHAR();
                SKIP_SPACES();
                if (cc != '#' && cc != '\n')
                    location.fatal(tr("Trailing garbage"));

                /*
                  Here is the recursive call.
                 */
                load(location,
                      QFileInfo(QFileInfo(fileName).dir(), includeFile)
                      .filePath());
            }
            else {
                /*
                  It wasn't an include statement, so it;s something else.
                 */
                if (cc == '+') {
                    plus = true;
                    SKIP_CHAR();
                }
                if (cc != '=')
                    location.fatal(tr("Expected '=' or '+=' after key"));
                SKIP_CHAR();
                SKIP_SPACES();

                for (;;) {
                    if (cc == '\\') {
                        int metaCharPos;

                        SKIP_CHAR();
                        if (cc == '\n') {
                            SKIP_CHAR();
                        }
                        else if (cc > '0' && cc < '8') {
                            word += QChar(c.digitValue());
                            SKIP_CHAR();
                        }
                        else if ((metaCharPos = QString(QLatin1String("abfnrtv")).indexOf(c)) != -1) {
                            word += "\a\b\f\n\r\t\v"[metaCharPos];
                            SKIP_CHAR();
                        }
                        else {
                            PUT_CHAR();
                        }
                    }
                    else if (c.isSpace() || cc == '#') {
                        if (inQuote) {
                            if (cc == '\n')
                                location.fatal(tr("Unterminated string"));
                            PUT_CHAR();
                        }
                        else {
                            if (!word.isEmpty()) {
                                if (metWord)
                                    stringValue += QLatin1Char(' ');
                                stringValue += word;
                                stringListValue << word;
                                metWord = true;
                                word.clear();
                                prevWordQuoted = false;
                            }
                            if (cc == '\n' || cc == '#')
                                break;
                            SKIP_SPACES();
                        }
                    }
                    else if (cc == '"') {
                        if (inQuote) {
                            if (!prevWordQuoted)
                                stringValue += QLatin1Char(' ');
                            stringValue += word;
                            if (!word.isEmpty())
                                stringListValue << word;
                            metWord = true;
                            word.clear();
                            prevWordQuoted = true;
                        }
                        inQuote = !inQuote;
                        SKIP_CHAR();
                    }
                    else if (cc == '$') {
                        QString var;
                        SKIP_CHAR();
                        while (c.isLetterOrNumber() || cc == '_') {
                            var += c;
                            SKIP_CHAR();
                        }
                        if (!var.isEmpty()) {
                            char *val = getenv(var.toLatin1().data());
                            if (val == 0) {
                                location.fatal(tr("Environment variable '%1' undefined").arg(var));
                            }
                            else {
                                word += QString(val);
                            }
                        }
                    }
                    else {
                        if (!inQuote && cc == '=')
                            location.fatal(tr("Unexpected '='"));
                        PUT_CHAR();
                    }
                }

                QStringList::ConstIterator key = keys.begin();
                while (key != keys.end()) {
                    if (!keySyntax.exactMatch(*key))
                        keyLoc.fatal(tr("Invalid key '%1'").arg(*key));

                    if (plus) {
                        if (locMap[*key].isEmpty()) {
                            locMap[*key] = keyLoc;
                        }
                        else {
                            locMap[*key].setEtc(true);
                        }
                        if (stringValueMap[*key].isEmpty()) {
                            stringValueMap[*key] = stringValue;
                        }
                        else {
                            stringValueMap[*key] +=
                                QLatin1Char(' ') + stringValue;
                        }
                        stringListValueMap[*key] += stringListValue;
                    }
                    else {
                        locMap[*key] = keyLoc;
                        stringValueMap[*key] = stringValue;
                        stringListValueMap[*key] = stringListValue;
                    }
                    ++key;
                }
            }
        }
        else {
            location.fatal(tr("Unexpected character '%1' at beginning of line")
                            .arg(c));
        }
    }
}
bool PageGenerator::parseArg(const QString& src,
                             const QString& tag,
                             int* pos,
                             int n,
                             QStringRef* contents,
                             QStringRef* par1,
                             bool debug)
{
#define SKIP_CHAR(c) \
    if (debug) \
        qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
    if (i >= n || src[i] != c) { \
        if (debug) \
            qDebug() << " char '" << c << "' not found"; \
        return false; \
    } \
    ++i;


#define SKIP_SPACE \
    while (i < n && src[i] == ' ') \
        ++i;

    int i = *pos;
    int j = i;

    // assume "<@" has been parsed outside
    //SKIP_CHAR('<');
    //SKIP_CHAR('@');

    if (tag != QStringRef(&src, i, tag.length())) {
        if (0 && debug)
            qDebug() << "tag " << tag << " not found at " << i;
        return false;
    }

    if (debug)
        qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;

    // skip tag
    i += tag.length();

    // parse stuff like:  linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
    if (par1) {
        SKIP_SPACE;
        // read parameter name
        j = i;
        while (i < n && src[i].isLetter())
            ++i;
        if (src[i] == '=') {
            if (debug)
                qDebug() << "read parameter" << QString(src.data() + j, i - j);
            SKIP_CHAR('=');
            SKIP_CHAR('"');
            // skip parameter name
            j = i;
            while (i < n && src[i] != '"')
                ++i;
            *par1 = QStringRef(&src, j, i - j);
            SKIP_CHAR('"');
            SKIP_SPACE;
        } else {
            if (debug)
                qDebug() << "no optional parameter found";
        }
    }
    SKIP_SPACE;
    SKIP_CHAR('>');

    // find contents up to closing "</@tag>
    j = i;
    for (; true; ++i) {
        if (i + 4 + tag.length() > n)
            return false;
        if (src[i] != '<')
            continue;
        if (src[i + 1] != '/')
            continue;
        if (src[i + 2] != '@')
            continue;
        if (tag != QStringRef(&src, i + 3, tag.length()))
            continue;
        if (src[i + 3 + tag.length()] != '>')
            continue;
        break;
    }

    *contents = QStringRef(&src, j, i - j);

    i += tag.length() + 4;

    *pos = i;
    if (debug)
        qDebug() << " tag " << tag << " found: pos now: " << i;
    return true;
#undef SKIP_CHAR
}
Beispiel #6
0
/*!
  Load, parse, and process a qdoc configuration file. This
  function is only called by the other load() function, but
  this one is recursive, i.e., it calls itself when it sees
  an \c{include} statement in the qdoc configuration file.
 */
void Config::load(Location location, const QString& fileName)
{
    QFileInfo fileInfo(fileName);
    QString path = fileInfo.canonicalPath();
    pushWorkingDir(path);
    QDir::setCurrent(path);
    QRegExp keySyntax(QLatin1String("\\w+(?:\\.\\w+)*"));

#define SKIP_CHAR() \
    do { \
    location.advance(c); \
    ++i; \
    c = text.at(i); \
    cc = c.unicode(); \
} while (0)

#define SKIP_SPACES() \
    while (c.isSpace() && cc != '\n') \
    SKIP_CHAR()

#define PUT_CHAR() \
    word += c; \
    SKIP_CHAR();

    if (location.depth() > 16)
        location.fatal(tr("Too many nested includes"));

    QFile fin(fileInfo.fileName());
    if (!fin.open(QFile::ReadOnly | QFile::Text)) {
        if (!Config::installDir.isEmpty()) {
            int prefix = location.filePath().length() - location.fileName().length();
            fin.setFileName(Config::installDir + QLatin1Char('/') + fileName.right(fileName.length() - prefix));
        }
        if (!fin.open(QFile::ReadOnly | QFile::Text))
            location.fatal(tr("Cannot open file '%1': %2").arg(fileName).arg(fin.errorString()));
    }

    QTextStream stream(&fin);
#ifndef QT_NO_TEXTCODEC
    stream.setCodec("UTF-8");
#endif
    QString text = stream.readAll();
    text += QLatin1String("\n\n");
    text += QLatin1Char('\0');
    fin.close();

    location.push(fileName);
    location.start();

    int i = 0;
    QChar c = text.at(0);
    uint cc = c.unicode();
    while (i < (int) text.length()) {
        if (cc == 0) {
            ++i;
        } else if (c.isSpace()) {
            SKIP_CHAR();
        } else if (cc == '#') {
            do {
                SKIP_CHAR();
            } while (cc != '\n');
        } else if (isMetaKeyChar(c)) {
            Location keyLoc = location;
            bool plus = false;
            QString stringValue;
            QStringList rhsValues;
            QString word;
            bool inQuote = false;
            bool prevWordQuoted = true;
            bool metWord = false;

            MetaStack stack;
            do {
                stack.process(c, location);
                SKIP_CHAR();
            } while (isMetaKeyChar(c));

            QStringList keys = stack.getExpanded(location);
            SKIP_SPACES();

            if (keys.count() == 1 && keys.first() == QLatin1String("include")) {
                QString includeFile;

                if (cc != '(')
                    location.fatal(tr("Bad include syntax"));
                SKIP_CHAR();
                SKIP_SPACES();

                while (!c.isSpace() && cc != '#' && cc != ')') {

                    if (cc == '$') {
                        QString var;
                        SKIP_CHAR();
                        while (c.isLetterOrNumber() || cc == '_') {
                            var += c;
                            SKIP_CHAR();
                        }
                        if (!var.isEmpty()) {
                            const QByteArray val = qgetenv(var.toLatin1().data());
                            if (val.isNull()) {
                                location.fatal(tr("Environment variable '%1' undefined").arg(var));
                            }
                            else {
                                includeFile += QString::fromLatin1(val);
                            }
                        }
                    } else {
                        includeFile += c;
                        SKIP_CHAR();
                    }
                }
                SKIP_SPACES();
                if (cc != ')')
                    location.fatal(tr("Bad include syntax"));
                SKIP_CHAR();
                SKIP_SPACES();
                if (cc != '#' && cc != '\n')
                    location.fatal(tr("Trailing garbage"));

                /*
                  Here is the recursive call.
                 */
                load(location, QFileInfo(QDir(path), includeFile).filePath());
            }
            else {
                /*
                  It wasn't an include statement, so it's something else.
                  We must see either '=' or '+=' next. If not, fatal error.
                 */
                if (cc == '+') {
                    plus = true;
                    SKIP_CHAR();
                }
                if (cc != '=')
                    location.fatal(tr("Expected '=' or '+=' after key"));
                SKIP_CHAR();
                SKIP_SPACES();

                for (;;) {
                    if (cc == '\\') {
                        int metaCharPos;

                        SKIP_CHAR();
                        if (cc == '\n') {
                            SKIP_CHAR();
                        }
                        else if (cc > '0' && cc < '8') {
                            word += QChar(c.digitValue());
                            SKIP_CHAR();
                        }
                        else if ((metaCharPos = QString::fromLatin1("abfnrtv").indexOf(c)) != -1) {
                            word += QLatin1Char("\a\b\f\n\r\t\v"[metaCharPos]);
                            SKIP_CHAR();
                        }
                        else {
                            PUT_CHAR();
                        }
                    }
                    else if (c.isSpace() || cc == '#') {
                        if (inQuote) {
                            if (cc == '\n')
                                location.fatal(tr("Unterminated string"));
                            PUT_CHAR();
                        }
                        else {
                            if (!word.isEmpty()) {
                                if (metWord)
                                    stringValue += QLatin1Char(' ');
                                stringValue += word;
#if 0
                                if (metWord)
                                    rhsValues << QString(" " + word);
                                else
#endif
                                rhsValues << word;
                                metWord = true;
                                word.clear();
                                prevWordQuoted = false;
                            }
                            if (cc == '\n' || cc == '#')
                                break;
                            SKIP_SPACES();
                        }
                    }
                    else if (cc == '"') {
                        if (inQuote) {
                            if (!prevWordQuoted)
                                stringValue += QLatin1Char(' ');
                            stringValue += word;
                            if (!word.isEmpty())
                                rhsValues << word;
                            metWord = true;
                            word.clear();
                            prevWordQuoted = true;
                        }
                        inQuote = !inQuote;
                        SKIP_CHAR();
                    }
                    else if (cc == '$') {
                        QString var;
                        SKIP_CHAR();
                        while (c.isLetterOrNumber() || cc == '_') {
                            var += c;
                            SKIP_CHAR();
                        }
                        if (!var.isEmpty()) {
                            const QByteArray val = qgetenv(var.toLatin1().constData());
                            if (val.isNull()) {
                                location.fatal(tr("Environment variable '%1' undefined").arg(var));
                            }
                            else {
                                word += QString::fromLatin1(val);
                            }
                        }
                    }
                    else {
                        if (!inQuote && cc == '=')
                            location.fatal(tr("Unexpected '='"));
                        PUT_CHAR();
                    }
                }

                QStringList::ConstIterator key = keys.constBegin();
                while (key != keys.constEnd()) {
                    if (!keySyntax.exactMatch(*key))
                        keyLoc.fatal(tr("Invalid key '%1'").arg(*key));

                    ConfigVarMultimap::Iterator i;
                    i = configVars_.insert(*key, ConfigVar(*key, rhsValues, QDir::currentPath(), keyLoc));
                    i.value().plus_ = plus;
                    ++key;
                }
            }
        } else {
            location.fatal(tr("Unexpected character '%1' at beginning of line").arg(c));
        }
    }
    popWorkingDir();
    if (!workingDirs_.isEmpty())
        QDir::setCurrent(workingDirs_.top());
}