/*
* $Id: routine.c,v 1.11 2025/05/06 15:18:55 snw Exp $
* Routine buffer management
*
*
* Author: Serena Willis <snw@coherent-logic.com>
* Copyright (C) 1998 MUG Deutschland
* Copyright (C) 2023, 2025 Coherent Logic Development LLC
*
*
* This file is part of FreeM.
*
* FreeM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FreeM 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 Affero Public License for more details.
*
* You should have received a copy of the GNU Affero Public License
* along with FreeM. If not, see <https://www.gnu.org/licenses/>.
*
* $Log: routine.c,v $
* Revision 1.11 2025/05/06 15:18:55 snw
* Remove isblank call from routine.c to fix broken Solaris 8 build
*
* Revision 1.10 2025/05/01 03:56:29 snw
* -m
*
* Revision 1.9 2025/04/30 20:03:09 snw
* Work on entryref parser
*
* Revision 1.8 2025/04/30 17:19:16 snw
* Improve backtraces in debugger
*
* Revision 1.7 2025/04/30 14:41:03 snw
* Further debugger work
*
* Revision 1.6 2025/04/13 04:22:43 snw
* Fix snprintf calls
*
* Revision 1.5 2025/04/09 19:52:02 snw
* Eliminate as many warnings as possible while building with -Wall
*
* Revision 1.4 2025/03/27 03:27:35 snw
* Install init scripts to share/freem/examples/init and fix regression in method dispatch
*
* Revision 1.3 2025/03/09 19:50:47 snw
* Second phase of REUSE compliance and header reformat
*
*
* SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
* SPDX-License-Identifier: AGPL-3.0-or-later
**/
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#if !defined(__OpenBSD__) && !defined(__FreeBSD__)
# include <sys/timeb.h>
#endif
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef AMIGA68K
#include <sys/fcntl.h>
#endif
#include "mpsdef.h"
#include <time.h>
#ifdef USE_SYS_TIME_H
#include <sys/time.h>
#endif
#include "events.h"
short rtn_get_offset(char *buf)
{
char *rp;
char *rc;
char *p;
char otag[256];
char ortn[256];
char oline[256];
register int i = 0;
register int j = 0;
register int k = 0;
int os = 0;
stcpy (ortn, rou_name);
rp = rouptr;
rc = roucur;
stcnv_m2c (ortn);
while (rp < rc) {
i = 0;
for (p = rp + 1; p < rc && *p != EOL && *p != '\0'; p++) {
if (i < 256) {
oline[i++] = *p;
}
}
oline[i] = '\0';
if (isalpha (oline[0]) || oline[0] == '%') {
os = 0;
k = 0;
for (j = 0; j < strlen (oline); j++) {
switch (oline[j]) {
case ' ':
case '(':
case ';':
case EOL:
otag[k] = '\0';
break;
default:
otag[k++] = oline[j];
}
if (oline[j] == ' ' || oline[j] == '(' || oline[j] == ';' || oline[j] == EOL) break;
}
}
else {
os++;
}
rp = p + 1;
}
if (os) {
sprintf (buf, "%s+%d^%s\201", otag, os, ortn);
}
else {
sprintf (buf, "%s^%s\201", otag, ortn);
}
return TRUE;
}
char *rtn_resolve(char *rou, char *tag, char *buf)
{
char superclass[255];
if (rtn_has_tag (rou, tag)) {
strcpy (buf, rou);
return buf;
}
else {
if (rtn_get_superclass (rou, superclass)) {
return rtn_resolve (superclass, tag, buf);
}
else {
buf = NULL;
return NULL;
}
}
}
short rtn_get_superclass(char *rou, char *buf)
{
FILE *fp;
char pth[PATHLEN];
char line[255];
char *s;
short rtn_exists;
short after_parens;
short found_super;
char *p;
register char ch;
if (strcmp (rou, "%OBJECT") == 0) {
buf = NULL;
return FALSE;
}
rtn_exists = rtn_get_path (rou, pth);
if (rtn_exists == FALSE) {
buf = NULL;
return FALSE;
}
fp = fopen (pth, "r");
if (fp == NULL) {
buf = NULL;
return FALSE;
}
s = fgets (line, 255, fp);
fclose (fp);
if (s == NULL) {
buf = NULL;
return FALSE;
}
if ((!isalpha (line[0])) && (line[0] != '%')) {
buf = NULL;
return FALSE;
}
p = line;
after_parens = FALSE;
found_super = FALSE;
while ((ch = *p++) != '\0') {
if (ch == ')') after_parens = TRUE;
/* ignore comments in search for superclass */
if (ch == ';' && after_parens == TRUE) {
found_super = FALSE;
break;
}
if (ch == ':' && after_parens == TRUE) {
strcpy (buf, p);
found_super = TRUE;
break;
}
}
if (!found_super) {
sprintf (buf, "%%OBJECT");
return TRUE;
}
p = buf;
for (;;) {
ch = *p;
if (ch == SP || ch == TAB || ch == ';' || ch == '\0' || ch == '\r' || ch == '\n') {
*p = '\0';
break;
}
p++;
}
return TRUE;
}
short rtn_get_path(char *rou, char *buf)
{
FILE *fp;
char pth[PATHLEN];
if (rou[0] == '%') {
stcpy (pth, rou0plib);
stcnv_m2c (pth);
}
else {
stcpy (pth, rou0path);
stcnv_m2c (pth);
}
snprintf (buf, PATHLEN - 1, "%s/%s.m", pth, rou);
if ((fp = fopen (buf, "r")) != NULL) {
(void) fclose (fp);
return TRUE;
}
else {
return FALSE;
}
}
short rtn_has_tag(char *rou, char *tag)
{
m_entry *entries;
m_entry *e;
entries = rtn_get_entries (rou);
for (e = entries; e != NULL; e = e->next) {
if (strcmp (tag, e->tag) == 0) {
rtn_free_entries (entries);
return TRUE;
}
}
rtn_free_entries (entries);
return FALSE;
}
void rtn_free_entries(m_entry *head)
{
m_entry *tmp;
while (head != NULL) {
tmp = head;
head = head->next;
free (tmp);
}
head = NULL;
}
m_entry *rtn_get_entries(char *rou)
{
FILE *fp;
char rou_path[PATHLEN];
m_entry *head = NULL;
m_entry *t;
register char ch;
register int i = 0;
register int j = 0;
char cur_line[255];
char cur_label[255];
int has_args = 0;
char *paren_pos;
if (rtn_get_path (rou, rou_path) == FALSE) {
return (m_entry *) NULL;
}
fp = fopen (rou_path, "r");
while (fgets (cur_line, 255, fp) != NULL) {
if (isalpha (cur_line[0]) || cur_line[0] == '%') {
has_args = 0;
j = 0;
for (i = 0; i < strlen (cur_line); i++) {
ch = cur_line[i];
switch (ch) {
case ')':
cur_label[j++] = ')';
case SP:
case TAB:
case EOL:
cur_label[j] = '\0';
j = 0;
if (strlen (cur_label)) {
t = (m_entry *) malloc (sizeof (m_entry));
NULLPTRCHK(t,"rtn_get_entries");
paren_pos = strchr (cur_label, '(');
if (paren_pos == NULL) {
/* not a formallist */
t->tag = (char *) malloc (sizeof (char) * (strlen (cur_label) + 1));
NULLPTRCHK(t->tag,"rtn_get_entries");
strcpy (t->tag, cur_label);
}
else {
/* a formallist */
char *toktmp;
toktmp = strdup (cur_label);
NULLPTRCHK(toktmp,"rtn_get_entries");
(void) strtok (toktmp, "(");
t->tag = malloc (sizeof (char) * (strlen (toktmp) + 1));
NULLPTRCHK(t->tag,"rtn_get_entries");
strcpy (t->tag, toktmp);
free (toktmp);
}
t->next = head;
head = t;
}
break;
case '(':
has_args++;
default:
cur_label[j++] = ch;
}
if (ch == SP || ch == TAB || ch == EOL) break;
}
}
}
fclose (fp);
return head;
}
void zload (char *rou) /* load routine in buffer */
{
FILE *infile;
short linelen;
char pgm[256];
char tmp1[256];
register long int i;
register long int j;
register long int ch;
char *savptr; /* save routine pointer */
long timex;
short altern = 0;
/* Routines are stored in routine buffers. If a routine is called
* we first look whether it's already loaded. If not, we look for
* the least recently used buffer and load it there. Besides
* dramatically improved performance there is little effect on
* the user. Sometimes you see an effect: if the program is changed
* by some other user or by yourself using the 'ced' editor you
* may get the old version for some time with DO, GOTO or ZLOAD.
* A ZREMOVE makes sure the routine is loaded from disk.
*/
if (*rou == EOL || *rou == 0) { /* routine name empty */
pgms[0][0] = EOL;
rouend = rouins = rouptr = buff;
roucur = buff + (NO_OF_RBUF * PSIZE0 + 1);
*rouptr = EOL;
*(rouptr + 1) = EOL;
*(rouptr + 2) = EOL;
dosave[0] = 0;
return;
}
savptr = rouptr;
/* what time is it ? */
timex = time (0L);
/* FreeM: it takes a lickin' and keeps on tickin' */
/* let's have a look whether we already have the stuff */
for (i = 0; i < NO_OF_RBUF; i++) {
if (pgms[i][0] == 0) {
altern = i;
break;
} /* buffer empty */
j = 0;
while (rou[j] == pgms[i][j]) {
if (rou[j++] == EOL) {
rouptr = buff + (i * PSIZE0);
ages[i] = time (0L);
rouend = ends[i];
rouins = rouend - 1;
return;
}
}
if (ages[i] <= timex) timex = ages[altern = i];
}
/* clear DO-label stored under FOR */
dosave[0] = 0;
j = 0;
ch = EOL; /* init for multiple path search */
tmp1[0] = EOL;
nextpath: /* entry point for retry */
i = 0;
if (rou[0] == '%') { /* %_routines are in special directory */
if (mcmnd >= 'a') { /* DO GOTO JOB */
if (rou0plib[j] != EOL) {
while ((ch = pgm[i++] = rou0plib[j++]) != ':' && ch != EOL);
}
}
else if (rou1plib[j] != EOL) {
while ((ch = pgm[i++] = rou1plib[j++]) != ':' && ch != EOL);
}
}
else {
if (mcmnd >= 'a') { /* DO GOTO JOB */
if (rou0path[j] != EOL) {
while ((ch = pgm[i++] = rou0path[j++]) != ':' && ch != EOL);
}
}
else if (rou1path[j] != EOL) {
while ((ch = pgm[i++] = rou1path[j++]) != ':' && ch != EOL);
}
}
if (i > 0) {
if (i == 1 || (i == 2 && pgm[0] == '.')) {
i = 0;
}
else {
pgm[i - 1] = '/';
}
}
pgm[i] = EOL;
stcpy (tmp1, pgm); /* directory where we search for the routine */
stcpy (&pgm[i], rou);
rouptr = buff + (altern * PSIZE0);
stcat (pgm, rou_ext);
pgm[stlen (pgm)] = NUL; /* append routine extension */
if ((infile = fopen (pgm, "r")) == NULL) {
rouptr = savptr;
if (ch != EOL) goto nextpath; /* try next access path */
stcpy (varerr, rou);
merr_raise (NOPGM);
return;
}
again:
linelen = 0;
savptr = rouend = rouptr;
for (i = 1; i < (PSIZE0 - 1); i++) {
*++rouend = ch = getc (infile);
if (ch == LF || ch == EOF) {
*rouend++ = EOL;
i++;
*savptr = i - linelen - 2;
savptr = rouend;
linelen = i;
if (ch == EOF) {
fclose (infile);
*rouend-- = EOL;
rouins = rouend - 1;
ends[altern] = rouend;
ages[altern] = time (0L);
stcpy (pgms[altern], rou);
stcpy (path[altern], tmp1);
rbuf_flags[altern].dialect = standard;
if (standard == D_FREEM) {
rbuf_flags[altern].standard = FALSE;
}
else {
rbuf_flags[altern].standard = TRUE;
}
return;
}
}
}
rouptr = savptr;
if (autorsize) {
while ((ch = getc (infile)) != EOF) {
i++;
if (ch == LF) i++;
} /* how big? */
i = ((i + 3) & ~01777) + 02000; /* round for full kB; */
if (newrsize (i, NO_OF_RBUF) == 0) { /* try to get more routine space. */
altern = 0;
ch = EOL;
fseek (infile, 0L, 0);
goto again;
}
}
fclose (infile);
goto pgmov;
pgmov:
/* program overflow error */
rouptr = rouins = rouend = savptr;
(*savptr++) = EOL;
*savptr = EOL;
for (i = 0; i < NO_OF_RBUF; i++) {
ages[i] = 0;
pgms[i][0] = 0;
}
pgms[i][0] = EOL;
rou_name[0] = EOL;
merr_raise (PGMOV);
return;
} /* end of zload() */
void zsave (char *rou) /* save routine on disk */
{
register int i;
register int j;
register int ch;
char tmp[256];
stcpy (tmp, rou); /* save name without path */
/* look whether we know where the routine came from */
if (zsavestrategy) { /* VIEW 133: remember ZLOAD directory on ZSAVE */
for (i = 0; i < NO_OF_RBUF; i++) {
if (pgms[i][0] == 0) break; /* buffer empty */
j = 0;
while (rou[j] == pgms[i][j]) {
if (rou[j++] == EOL) {
stcpy (rou, path[i]);
stcat (rou, tmp);
j = 0;
ch = 1; /* init for multiple path search */
goto try;
}
}
}
}
/* not found */
j = 0;
ch = EOL; /* init for multiple path search */
nextpath: /* entry point for retry */
if (tmp[0] == '%') {
if (rou1plib[0] != EOL) {
i = 0;
while ((ch = rou[i++] = rou1plib[j++]) != ':' && ch != EOL);
if (i == 1 || (i == 2 && rou[0] == '.')) {
i = 0;
}
else {
rou[i - 1] = '/';
}
stcpy (&rou[i], tmp);
}
}
else {
if (rou1path[0] != EOL) {
i = 0;
while ((ch = rou[i++] = rou1path[j++]) != ':' && ch != EOL);
if (i == 1 || (i == 2 && rou[0] == '.')) {
i = 0;
}
else {
rou[i - 1] = '/';
}
stcpy (&rou[i], tmp);
}
}
try:
stcat (rou, rou_ext);
rou[stlen (rou)] = NUL; /* append routine extention */
if (rouend <= rouptr) {
unlink (rou);
rou_name[0] = EOL;
}
else {
FILE *outfile;
char *i0;
for (;;) {
errno = 0;
if ((outfile = fopen (rou, "w")) != NULL) break;
if (errno == EINTR) continue; /* interrupt */
if (errno == EMFILE || errno == ENFILE) {
close_all_globals ();
continue;
} /* free file_des */
if (ch != EOL) goto nextpath; /* try next access path */
merr_raise (PROTECT);
return;
}
i0 = rouptr;
while (++i0 < (rouend - 1)) {
if ((ch = (*(i0))) == EOL) {
ch = LF;
i0++;
}
putc (ch, outfile);
}
if (ch != LF) putc (LF, outfile);
fclose (outfile);
}
return;
} /* end of zsave() */
/* insert 'line' in routine at 'position' */
void zi (char *line, char *position)
{
short offset;
short label;
short i;
short i0;
short ch;
char *reg;
char *end;
char line0[256];
if (rouend - rouptr + stlen (line) + 1 > PSIZE0) { /* sufficient space ??? */
reg = buff;
if (getrmore () == 0L) return; /* PGMOV */
position += buff - reg;
}
label = TRUE;
i = 0;
i0 = 0;
while ((ch = line[i]) != EOL) {
if (label) {
if (ch == SP) ch = TAB;
if (ch == TAB) {
label = FALSE;
}
else if (ch == '(') {
line0[i0++] = ch;
i++;
while (((ch = line[i]) >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '%' || ch == ',') {
line0[i0++] = ch;
i++;
}
if (ch != ')') {
merr_raise (ISYNTX);
return;
}
line0[i0++] = ch;
i++;
if ((ch = line[i]) != SP && ch != TAB) {
merr_raise (ISYNTX);
return;
}
continue;
}
else if ((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9') && (ch != '%' || i)) {
merr_raise (ISYNTX);
return;
}
line0[i0++] = ch;
i++;
continue;
}
if (ch < SP || (ch >= DEL && (eightbit == FALSE))) {
merr_raise (ISYNTX);
return;
}
line0[i0++] = ch;
i++;
}
if (label) {
merr_raise (ISYNTX);
return;
}
line0[i0] = EOL;
offset = i0;
if (offset > 0) {
offset += 2;
end = rouend;
rouend += offset;
if (roucur > position || roucur > end) roucur += offset;
reg = rouend;
while (position <= end) {
(*reg--) = (*end--);
}
(*(position++)) = (UNSIGN (offset) - 2);
reg = line0;
while (((*(position++)) = (*(reg++))) != EOL);
*(rouend + 1) = EOL;
*(rouend + 2) = EOL;
for (i = 0; i < NO_OF_RBUF; i++) {
if (rouptr == (buff + (i * PSIZE0))) {
ends[i] = rouend;
break;
}
}
}
rouins = position;
return;
} /* end of zi() */
/*
* getraddress(char *a, short lvl):
*
* returns the 'canonical' address of the line at the specified DO/FOR/XEC level
*
* char *a (out param): pointer to the address of the line
* short lvl: process this level
*
*/
void getraddress (char *a, short lvl)
{
char *rcur; /* cursor into routine */
short f;
char tmp3[256];
char *j0;
char *j1;
short rlvl; /* lower level, where to find routine name */
register int i;
register int j;
f = mcmnd;
mcmnd = 'd'; /* make load use standard-path */
rlvl = lvl;
if (nestn[rlvl] == 0 && rlvl < nstx) rlvl++;
if (nestn[rlvl]) zload (nestn[rlvl]);
mcmnd = f;
/* command on stack: 2 == DO_BLOCK; other: make uppercase */
i = nestc[lvl];
if (i != '$') i = ((i == 2) ? 'd' : i - 32);
a[0] = '(';
a[1] = i;
a[2] = ')';
a[3] = EOL; /* command */
rcur = nestr[lvl] + rouptr; /* restore rcur */
j0 = (rouptr - 1);
j = 0;
tmp3[0] = EOL;
j0++;
if (rcur < rouend) {
while (j0 < (rcur - 1)) {
j1 = j0++;
j++;
if ((*j0 != TAB) && (*j0 != SP)) {
j = 0;
while ((tmp3[j] = (*(j0++))) > SP) {
if (tmp3[j] == '(') tmp3[j] = EOL;
j++;
}
tmp3[j] = EOL;
j = 0;
}
j0 = j1;
j0 += (UNSIGN (*j1)) + 2;
}
}
stcat (a, tmp3);
if (j > 0) {
i = stlen (a);
a[i++] = '+';
intstr (&a[i], j);
}
if (nestn[rlvl]) {
stcat (a, "^\201");
stcat (a, nestn[rlvl]);
}
else if (rou_name[0] != EOL) {
stcat (a, "^\201");
stcat (a, rou_name);
}
f = mcmnd;
mcmnd = 'd'; /* make load use standard-path */
zload (rou_name);
mcmnd = f;
return;
} /* end getraddress() */
/* parse lineref and return pos.in routine */
/* result: [pointer to] pointer to line */
void lineref (char **adrr)
{
long offset;
long j;
char *reg;
char *beg;
while (*codptr == '@') { /* handle indirection */
codptr++;
expr (ARGIND);
if (merr () > 0) return;
stcat (argptr, codptr);
stcpy (code, argptr);
codptr = code;
}
offset = 0;
beg = rouptr;
if (*codptr == '+') {
codptr++;
expr (STRING);
if (merr () > 0) return;
if ((offset = intexpr (argptr)) <= 0) {
*adrr = 0;
return;
}
offset--;
}
else {
expr (LABEL);
if (merr () > 0) return;
reg = beg;
while (beg < rouend) {
reg++;
if ((*reg) != TAB && (*reg) != SP) {
j = 0;
while ((*reg) == varnam[j]) {
reg++;
j++;
}
if (((*reg) == TAB || (*reg) == SP || (*reg) == '(') && varnam[j] == EOL) break;
}
reg = (beg = beg + UNSIGN (*beg) + 2);
}
stcpy (varerr, varnam);
varnam[0] = EOL;
codptr++;
if (*codptr == '+') {
codptr++;
expr (STRING);
if (merr () > 0) return;
offset = intexpr (argptr);
}
}
if (offset < 0) {
reg = rouptr;
while (reg < beg) {
reg += UNSIGN (*reg) + 2;
offset++;
}
if (offset < 0) {
*adrr = 0;
return;
}
beg = rouptr;
}
while (offset-- > 0 && beg <= rouend) beg += UNSIGN (*beg) + 2;
*adrr = beg;
return;
} /* end of lineref() */
char *m_text(char *lref, char *buf)
{
char *old_codptr;
char old_code[STRLEN];
old_codptr = codptr;
stcpy (old_code, code);
sprintf (code, "$TEXT(%s)\201", lref);
codptr = code;
expr (STRING);
if (merr () != OK) {
stcpy (code, old_code);
codptr = old_codptr;
return (char *) NULL;
}
stcpy (buf, argptr);
stcpy (code, old_code);
codptr = old_codptr;
return buf;
}
/* routine_get_line()
* char *entryref: [cstr/in] M entryref
* char *buf: [cstr/out] buffer to contain resulting line
*
* returns a pointer to buf [cstr] on success; otherwise NULL */
char *routine_get_line(char *entryref, char *buf)
{
FILE *fp;
char routine_path[PATHLEN];
char *routine_p;
char *label_p;
char *offset_p;
short have_label = FALSE;
short have_offset = FALSE;
char r[256];
char l[256];
char o[256];
char txtbuf[256];
char line[2048];
int curline;
char curtag[256];
int cur_offset;
int os;
register int i;
register int j;
char ch;
short in_tag = FALSE;
/*
* entryref can be:
* +0 (returns rou_name)
* label
* ^routine
* +offset
* +offset^routine
* label^routine
* label+offset^routine
*/
/* handle +0 */
if (strcmp (entryref, "+0") == 0) {
stcpy (buf, rou_name);
stcnv_m2c (buf);
return buf;
}
if ((routine_p = strchr (entryref, '^')) != NULL) {
/* have a routine */
stcpy (r, routine_p);
stcnv_m2c (r);
if ((entryref[0] != '^') && (isalpha (entryref[0]))) {
/* we have a label */
char ch;
char *src;
char *dst;
src = entryref;
dst = l;
while (((ch = *src++) != '\0') && (ch != '^') && (ch != '+')) {
*dst++ = ch;
}
*dst = '\0';
have_label = TRUE;
}
}
else {
/* no routine implies current routine */
stcpy (r, rou_name);
stcnv_m2c (r);
}
if (r[0] == '^') {
strcpy (txtbuf, &(r[1]));
strcpy (r, txtbuf);
}
if (rtn_get_path (r, routine_path) == FALSE) {
sprintf (buf, "");
return NULL;
}
if ((fp = fopen (routine_path, "r")) == NULL) {
sprintf (buf, "");
return NULL;
}
if ((offset_p = strchr (entryref, '+')) != NULL) {
stcpy (o, offset_p + 1);
os = atoi (o);
sprintf (o, "%d\0", os);
have_offset = TRUE;
}
else {
have_offset = FALSE;
os = 1;
}
in_tag = FALSE;
cur_offset = 0;
while (fgets (line, sizeof (line) - 1, fp) != NULL) {
if (!have_label && !have_offset) {
strcpy (buf, line);
fclose (fp);
return buf;
}
if ((isalpha (line[0])) && (line[0] != ' ') && (line[0] != '\t')) {
j = 0;
for (i = 0; i < strlen (line); i++) {
ch = line[i];
if ((!isalpha (ch)) && (!isdigit (ch))) {
curtag[j] = '\0';
cur_offset = 0;
if (have_label && (strcmp (curtag, l) == 0)) {
in_tag = TRUE;
if (!have_offset) {
strcpy (buf, line);
fclose (fp);
return buf;
}
}
else {
in_tag = FALSE;
}
break;
}
else {
curtag[j++] = ch;
}
}
}
else {
cur_offset++;
if (in_tag && (cur_offset == os)) {
strcpy (buf, line);
fclose (fp);
return buf;
}
}
}
fclose (fp);
return FALSE;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>