Initial revision

This commit is contained in:
Jim Kowalkowski
1994-05-06 10:38:38 +00:00
parent 6ef0b24bae
commit 96fb364371
5 changed files with 1077 additions and 0 deletions
+308
View File
@@ -0,0 +1,308 @@
%{
/**************************************************************************
*
* Author: Jim Kowalkowski
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 10-29-93 jbk initial version
*
***********************************************************************/
#include <stdio.h>
#include <string.h>
#ifdef vxWorks
#include <memLib.h>
#else
#include <memory.h>
#include <malloc.h>
#endif
#include "dbVarSub.h"
#include <dbStaticLib.h>
#include <epicsVersion.h>
static char subst_buffer[VAR_MAX_SUB_SIZE];
static int subst_used;
static int line_num;
static int yyerror();
static void sub_pvname(char*,char*);
#ifdef vxWorks
static DBENTRY* pdbentry;
extern struct dbBase *pdbBase;
#endif
%}
%start database
%token <Str> COMMA
%token <Str> WORD VALUE
%token <Str> FIELD
%left O_BRACE C_BRACE O_PAREN C_PAREN
%left DATABASE CONTAINER RECORD
%left NOWHERE
%union
{
int Int;
char Char;
char *Str;
double Real;
}
%%
database: DATABASE d_head d_body
{
#ifdef vxWorks
dbFreeEntry(pdbentry);
#endif
}
| DATABASE d_head /* jbk added for graphical thing */
{
#ifdef vxWorks
dbFreeEntry(pdbentry);
#endif
}
;
d_head: O_PAREN WORD C_PAREN
{
#ifdef vxWorks
fprintf(stderr,"Warning: No EPICS version information in db file\n");
pdbentry=dbAllocEntry(pdbBase);
free($2);
#endif
}
| O_PAREN WORD COMMA VALUE C_PAREN
{
#ifdef vxWorks
int version,revision;
char* v;
v=strtok($4," ."); sscanf(v,"%d",&version);
v=strtok(NULL," ."); sscanf(v,"%d",&revision);
if(version!=EPICS_VERSION || revision!=EPICS_REVISION)
fprintf(stderr,"Warning: Database not created with same version\n");
pdbentry=dbAllocEntry(pdbBase);
free($2); free($4);
#endif
}
;
d_body: O_BRACE nowhere_records db_components C_BRACE
;
/* nowhere is here for back compatability */
nowhere_records: /* null */
| NOWHERE n_head n_body
;
n_head: O_PAREN C_PAREN
;
n_body: O_BRACE records C_BRACE
;
db_components: /* null */
| db_components container
| db_components record
;
container: CONTAINER c_head c_body
;
c_head: O_PAREN WORD C_PAREN
{
free($2);
}
;
c_body: O_BRACE db_components C_BRACE
;
records: /* null */
| records record
;
record: RECORD r_head r_body
{
#ifndef vxWorks
printf(" }\n");
#endif
}
;
r_head: O_PAREN WORD COMMA WORD C_PAREN
{
sub_pvname($2,$4);
free($2); free($4);
}
| O_PAREN WORD COMMA VALUE C_PAREN
{
sub_pvname($2,$4);
free($2); free($4);
}
;
r_body: /* null */
| O_BRACE fields C_BRACE
;
fields: /* null */
| fields field
;
field: FIELD O_PAREN WORD COMMA VALUE C_PAREN
{
#ifdef vxWorks
if( dbFindField(pdbentry,$<Str>3) )
fprintf(stderr,"Cannot find field %s\n",$<Str>3);
#endif
if(subst_used)
{
strcpy(subst_buffer,$<Str>5);
if(dbDoSubst(subst_buffer,sizeof(subst_buffer),NULL)!=0)
fprintf(stderr,"dbDoSubst failed\n");
#ifdef vxWorks
if( dbPutString(pdbentry, subst_buffer) )
fprintf(stderr,"Cannot set field %s to %s\n",
$<Str>3,subst_buffer);
#else
printf("\n\t\tfield(%s, \"%s\")",$<Str>3,subst_buffer);
#endif
}
else
{
#ifdef vxWorks
if( dbPutString(pdbentry, $<Str>5) )
fprintf(stderr,"Cannot set field %s to %s\n",$<Str>3,$<Str>5);
#else
printf("\n\t\tfield(%s, \"%s\")",$<Str>3,$<Str>5);
#endif
}
free($3); free($5);
}
;
%%
#include "dbLoadRecords_lex.c"
static int yyerror(str)
char *str;
{ fprintf(stderr,"db file parse, Error line %d : %s\n",line_num, yytext); }
static int is_not_inited = 1;
int dbLoadRecords(char* pfilename, char* pattern, char* container)
{
FILE* fp;
long status;
#ifdef vxWorks
if(pdbBase==NULL)
{
fprintf(stderr,"dbLoadRecords: default.dctsdr file not loaded\n");
return -1;
}
#endif
if( pattern && *pattern )
{
subst_used = 1;
dbInitSubst(pattern);
}
else
subst_used = 0;
if( !(fp=fopen(pfilename,"r")) )
{
fprintf(stderr,"dbLoadRecords: error opening file\n");
return -1;
}
#ifndef vxWorks
/* if(container) printf(" %s {\n",container); */
#endif
if(is_not_inited)
{
yyin=fp;
is_not_inited=0;
}
else
{
yyrestart(fp);
}
yyparse();
#ifndef vxWorks
/* if(container) printf(" }\n"); */
#endif
if(subst_used) dbFreeSubst();
fclose(fp);
return 0;
}
static void sub_pvname(char* type, char* name)
{
#ifdef vxWorks
if( dbFindRecdes(pdbentry,type) )
fprintf(stderr,"Cannot find record type %s\n",type);
#endif
if(subst_used)
{
strcpy(subst_buffer,name);
if(dbDoSubst(subst_buffer,PVNAME_STRINGSZ,NULL)!=0)
fprintf(stderr,"dbDoSubst failed\n");
#ifdef vxWorks
if( dbCreateRecord(pdbentry,subst_buffer) )
fprintf(stderr,"Cannot create record %s\n",subst_buffer);
#else
printf("\trecord(%s, \"%s\") {",type,subst_buffer);
#endif
}
else
{
#ifdef vxWorks
if( dbCreateRecord(pdbentry,name) )
fprintf(stderr,"Cannot create record %s\n",name);
#else
printf("\trecord(%s, \"%s\") {",type,name);
#endif
}
}
+29
View File
@@ -0,0 +1,29 @@
pvname [a-zA-Z0-9_\-:\.\[\]<>]
value [a-zA-Z0-9_\,\./\*#\[\]%: ;!|\'\-&\(\)@\?\+<>=\$\{\}]
%{
%}
%%
"field" { return(FIELD); }
"record" { return(RECORD); }
"container" { return(CONTAINER); }
"database" { return(DATABASE); }
"nowhere" { return(NOWHERE); }
\"{value}*\" { yylval.Str=(char *)malloc(strlen(yytext)+1); strcpy(yylval.Str,yytext+1); yylval.Str[strlen(yylval.Str)-1] = '\0'; return(VALUE); }
{pvname}+ { yylval.Str=(char *)malloc(strlen(yytext)+1); strcpy(yylval.Str,yytext); return(WORD); }
"{" { return(O_BRACE); }
"}" { return(C_BRACE); }
"(" { return(O_PAREN); }
")" { return(C_PAREN); }
"," { return(COMMA); }
. ;
\n { line_num ++;}
%%
+396
View File
@@ -0,0 +1,396 @@
%{
/**************************************************************************
*
* Author: Jim Kowalkowski
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 10-29-93 jbk initial version
*
***********************************************************************/
#include <stdio.h>
#include <string.h>
#ifdef vxWorks
#include <memLib.h>
#include <stdlib.h>
#else
#include <memory.h>
#include <malloc.h>
#endif
#include "dbVarSub.h"
#include <epicsVersion.h>
static int line_num;
static int yyerror();
int dbLoadTemplate(char* sub_file);
#ifdef SUB_TOOL
static int sub_it();
#else
int dbLoadRecords(char* pfilename, char* pattern, char* container);
#endif
static char sub_collect[VAR_MAX_VAR_STRING];
static char** vars;
static char* db_file_name = (char*)NULL;
static int var_count,sub_count;
%}
%start template
%token <Str> WORD QUOTE
%token DBFILE
%token PATTERN
%token EQUALS
%left O_PAREN C_PAREN
%left O_BRACE C_BRACE
%union
{
int Int;
char Char;
char *Str;
double Real;
}
%%
template: templs
| subst
;
templs: templs templ
| templ
;
templ: templ_head O_BRACE subst C_BRACE
;
templ_head: DBFILE WORD
{
var_count=0;
if(db_file_name) free(db_file_name);
db_file_name=$2;
}
;
subst: PATTERN pattern subs
| var_subs
;
pattern: O_BRACE vars C_BRACE
{
/*
int i;
for(i=0;i<var_count;i++) fprintf(stderr,"variable=(%s)\n",vars[i]);
fprintf(stderr,"var_count=%d\n",var_count);
*/
}
;
vars: vars var
| var
;
var: WORD
{
vars[var_count++]=$1;
}
;
subs: subs sub
| sub
;
sub: WORD O_BRACE vals C_BRACE
{
sub_collect[strlen(sub_collect)-1]='\0';
/* fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect); */
#ifndef SUB_TOOL
if(db_file_name)
dbLoadRecords(db_file_name,sub_collect,$1);
else
fprintf(stderr,"Error: no db file name given\n");
#else
sub_it();
#endif
free($1);
sub_collect[0]='\0';
sub_count=0;
}
| O_BRACE vals C_BRACE
{
sub_collect[strlen(sub_collect)-1]='\0';
/* fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect); */
#ifndef SUB_TOOL
if(db_file_name)
dbLoadRecords(db_file_name,sub_collect,NULL);
else
fprintf(stderr,"Error: no db file name given\n");
#else
sub_it();
#endif
sub_collect[0]='\0';
sub_count=0;
}
;
vals: vals val
| val
;
val: QUOTE
{
if(sub_count<=var_count)
{
strcat(sub_collect,vars[sub_count]);
strcat(sub_collect,"=\"");
strcat(sub_collect,$1);
strcat(sub_collect,"\",");
free($1);
sub_count++;
}
}
| WORD
{
if(sub_count<=var_count)
{
strcat(sub_collect,vars[sub_count]);
strcat(sub_collect,"=");
strcat(sub_collect,$1);
strcat(sub_collect,",");
free($1);
sub_count++;
}
}
;
var_subs: var_subs var_sub
| var_sub
;
var_sub: WORD O_BRACE sub_pats C_BRACE
{
sub_collect[strlen(sub_collect)-1]='\0';
/* fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect); */
#ifndef SUB_TOOL
if(db_file_name)
dbLoadRecords(db_file_name,sub_collect,$1);
else
fprintf(stderr,"Error: no db file name given\n");
#else
sub_it();
#endif
free($1);
sub_collect[0]='\0';
sub_count=0;
}
| O_BRACE sub_pats C_BRACE
{
sub_collect[strlen(sub_collect)-1]='\0';
/* fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect); */
#ifndef SUB_TOOL
if(db_file_name)
dbLoadRecords(db_file_name,sub_collect,NULL);
else
fprintf(stderr,"Error: no db file name given\n");
#else
sub_it();
#endif
sub_collect[0]='\0';
sub_count=0;
}
;
sub_pats: sub_pats sub_pat
| sub_pat
;
sub_pat: WORD EQUALS WORD
{
strcat(sub_collect,$1);
strcat(sub_collect,"=");
strcat(sub_collect,$3);
strcat(sub_collect,",");
free($1); free($3);
sub_count++;
}
| WORD EQUALS QUOTE
{
strcat(sub_collect,$1);
strcat(sub_collect,"=\"");
strcat(sub_collect,$3);
strcat(sub_collect,"\",");
free($1); free($3);
sub_count++;
}
;
%%
#include "dbLoadTemplate_lex.c"
static int yyerror(str)
char *str;
{ fprintf(stderr,"templ file parse, Error line %d : %s\n",line_num, yytext); }
static int is_not_inited = 1;
int dbLoadTemplate(char* sub_file)
{
FILE *fp;
long status;
if( !sub_file || !*sub_file)
{
fprintf(stderr,"must specify variable substitution file\n");
return -1;
}
if( !(fp=fopen(sub_file,"r")) )
{
fprintf(stderr,"dbLoadTemplate: error opening sub file %s\n",sub_file);
return -1;
}
vars = (char**)malloc(VAR_MAX_VARS * sizeof(char*));
sub_collect[0]='\0';
var_count=0;
sub_count=0;
if(is_not_inited)
{
yyin=fp;
is_not_inited=0;
}
else yyrestart(fp);
yyparse();
free(vars);
fclose(fp);
return 0;
}
#ifndef vxWorks
#ifdef SUB_TOOL
/* this is generic substitution on any file */
char* dbfile;
main(int argc, char** argv)
{
if(argc!=3)
{
fprintf(stderr,"Usage: %s file sub_file\n",argv[0]);
fprintf(stderr,"\n\twhere file is any ascii text file\n");
fprintf(stderr,"\tsub_file in the variable substitution file\n");
fprintf(stderr,"\n\tThis program uses the sub_file to perform\n");
fprintf(stderr,"\tsubstitutions on to standard out.\n");
exit(1);
}
dbfile = argv[1];
dbLoadTemplate(argv[2]);
return 0;
}
/* use sub_collect and db_file_name to do work */
int sub_it()
{
FILE* fp;
char var_buff[500];
if( *sub_collect )
dbInitSubst(sub_collect);
else
{
fprintf(stderr,"No valid substitutions found in table\n");
exit(1);
}
if( !(fp=fopen(dbfile,"r")) )
{
fprintf(stderr,"sub_tool: error opening file\n");
exit(1);
}
/* do the work here */
while( fgets(var_buff,200,fp)!=(char*)NULL )
{
dbDoSubst(var_buff,500,NULL);
fputs(var_buff,stdout);
}
dbFreeSubst();
fclose(fp);
return 0;
}
#else
/* this is template loader similar to vxWorks one for .db files */
main(int argc, char** argv)
{
extern char* optarg;
extern int optind;
char* name = (char*)NULL;
int no_error = 1;
int c;
while(no_error && (c=getopt(argc,argv,"s:"))!=-1)
{
switch(c)
{
case 's':
if(name) free(name);
name = strdup(optarg);
break;
default: no_error=0; break;
}
}
if(!no_error || optind>=argc)
{
fprintf(stderr,"Usage: %s <-s name> sub_file\n",argv[0]);
fprintf(stderr,"\n\twhere name is the output database name and\n");
fprintf(stderr,"\tsub_file in the variable substitution file\n");
fprintf(stderr,"\n\tThis program used the sub_file to produce a\n");
fprintf(stderr,"\tdatabase of name name to standard out.\n");
exit(1);
}
if(!name) name = "Composite";
printf("database(name,\"%d.%d\") {\n",EPICS_VERSION,EPICS_REVISION,name);
dbLoadTemplate(argv[1]);
printf("}\n");
}
#endif
#endif
+27
View File
@@ -0,0 +1,27 @@
value [a-zA-Z0-9_\,\./\*#\[\]%: ;!|\-&\(\)@\?\+<>=\$]
word [a-zA-Z0-9_\./\*#\[\]%:;!|\-&\$\(\)@\?\+<>]
par [\"\']
%{
%}
%%
"pattern" { return(PATTERN); }
"file" { return(DBFILE); }
"=" { return(EQUALS); }
{par}{value}*{par} { yylval.Str=(char *)malloc(strlen(yytext)+1); strcpy(yylval.Str,yytext+1); yylval.Str[strlen(yylval.Str)-1] = '\0'; return(QUOTE); }
{word}+ { yylval.Str=(char *)malloc(strlen(yytext)+1); strcpy(yylval.Str,yytext); return(WORD); }
"(" { return(O_PAREN); }
")" { return(C_PAREN); }
"{" { return(O_BRACE); }
"}" { return(C_BRACE); }
. ;
\n { line_num ++;}
%%
+317
View File
@@ -0,0 +1,317 @@
/**************************************************************************
*
* Author: Jim Kowalkowski
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 10-29-93 jbk initial version
*
***********************************************************************/
#include <stdio.h>
#include <string.h>
#ifdef vxWorks
#include <memLib.h>
#endif
#include "dbVarSub.h"
#include <dbStaticLib.h>
static int subst_total;
static struct var_sub *subst = (struct var_sub*)NULL;
static char* pattern;
static char* get_var(char**, char*);
static char* get_sub(char*, char*);
/* ------------------ variable substitution routines --------------*/
#ifdef vxWorks
static char* strdup(char*p) { return strcpy((char*)malloc(strlen(p)+1),p); }
#endif
void dbFreeSubst()
{
int i;
if(subst)
{
free(pattern);
free(subst);
subst = (struct var_sub*)NULL;
subst_total = 0;
}
}
long dbDoSubst(char* replace, int size, struct var_tree* par)
{
/* perform substitution */
char preal[VAR_MAX_SUB_SIZE];
char pvar[VAR_MAX_SUB_SIZE];
char *to,*from,*pp;
char *hold = NULL;
int i,num_chars;
struct var_tree* test_var;
struct var_tree my_tree;
int l;
my_tree.parent = par;
l = strlen(replace);
for(num_chars=0,to=preal,from=replace;from<=(replace+l);)
{
/* see if this is really a variable */
if(from[0]==VAR_START_IND && from[1]==VAR_LEFT_IND)
{
/* found a variable */
from += 2;
if( !(pp=strchr(from,VAR_RIGHT_IND)) )
{
fprintf(stderr,
"dbDoSubst: Improper variable specification: %s\n",
from);
return -1;
}
*pp++ = '\0'; /* clear the closing paren for variable */
for(i=0;i<subst_total&&strcmp(subst[i].var,from);i++);
if(i<subst_total)
{
/* found a substitution */
strcpy(pvar,subst[i].sub);
/* check for looping in substitution */
my_tree.me=i;
for(test_var=par;test_var;test_var=test_var->parent)
{
if(test_var->me==i)
{
fprintf(stderr,
"dbDoSubst: recursive definition of variable %s\n",
from);
return -1;
}
}
/* check for successful substitution */
if(dbDoSubst(pvar,sizeof(pvar),&my_tree)<0) return -1;
/* copy substitution to output string */
for(hold=pvar;*to=*hold++;num_chars++,to++)
{
if(num_chars>size)
{
fprintf(stderr,
"dbDoSubst: substitution to long: %s), max=%d (r)\n",
replace,size);
return -1;
}
}
}
else
{
/* did not find a substitution */
#ifdef vxWorks
fprintf(stderr,"dbDoSubst: did not find sub for %s\n",from);
#endif
/* copy substitution to output string - this sucks */
/* the "$()" part of the variable must be re-added */
num_chars+=3; /* adjust for $() part */
*to++='$'; /* put the variable $ back in */
*to++='('; /* put the variable openning paren back in */
for(hold=from;*to=*hold++;num_chars++,to++)
{
if(num_chars>size)
{
fprintf(stderr,
"dbDoSubst: substitution to long: %s), max=%d (e)\n",
replace,size);
return -1;
}
}
*to++=')'; /* put the variable closing paren back in */
}
from = pp;
}
else
{
*to++ = *from++;
if(num_chars++>size)
{
fprintf(stderr,
"dbDoSubst: substitution to long for %s\n",
replace);
return -1;
}
}
}
strcpy(replace,preal);
return 0;
}
long dbInitSubst(char* parm_pattern)
{
char *pp,*hold;
int rc,pi,pass;
enum { var,sub } state;
/* --------- parse the pattern --------- */
rc=0;
if(parm_pattern && *parm_pattern)
{
pattern = strdup(parm_pattern);
dbFreeSubst();
/* count the number of variables in the pattern (use the = sign) */
for(subst_total=0,pp=pattern; *pp ;pp++)
{
/* find vars and subs */
switch(*pp)
{
case '\\': pp++; break;
case '=': subst_total++; break;
case '\"': for(++pp;*pp!='\"';pp++) if(*pp=='\\') pp++; pp++; break;
default: break;
}
}
/* fprintf(stderr,"total = %d\n",subst_total); */
/* allocate the substitution table */
subst = (struct var_sub*)malloc( sizeof(struct var_sub)*subst_total );
/* fill table from pattern - this is kind-of putrid */
subst_total=0;
pp=pattern;
state=var;
while(*pp)
{
switch(*pp)
{
case ' ':
case ',':
case '\t': pp++; break;
case '\\': pp+=2; break;
case '=':
case '\"':
pp=get_sub(subst[subst_total++].sub,pp);
state=var;
break;
default:
if(state==var)
{
pp=get_var(&subst[subst_total].var,pp);
state=sub;
}
else
{
pp=get_sub(subst[subst_total++].sub,pp);
state=var;
}
break;
}
}
/* debug code */
/*
for(pi=0;pi<subst_total;pi++)
{
printf("table[%d]=(%s,%s)\n",pi,subst[pi].var,subst[pi].sub);
}
*/
/* resolve the multiple substitutions now */
for(pi=0;pi<subst_total;pi++)
{
if(dbDoSubst(subst[pi].sub,VAR_MAX_SUB_SIZE,(struct var_tree*)NULL)<0)
{
fprintf(stderr, "dbInitSubst: failed to build variable substitution table (%s)\n",subst[pi].sub);
rc = -1;
}
}
/* more debug code */
/*
for(pi=0;pi<subst_total;pi++)
{
printf("table[%d]=(%s,%s)\n",pi,subst[pi].var,subst[pi].sub);
}
*/
}
else
{
subst_total=0;
subst=(struct var_sub*)NULL;
}
return(rc);
}
/* put the pointer to the variable in "from" into "to" */
static char* get_var(char** to, char* from)
{
char* pp;
pp = strpbrk(from," \t=");
*pp = '\0';
pp++;
/* fprintf(stderr,"get_var: (%s)\n",from); */
*to=from;
return pp;
}
/* copy the substitution in "from" into "to" */
static char* get_sub(char* to, char* from)
{
char *pp,*hold;
char* cp = to;
for(pp=from;*pp==' ' || *pp=='\t' || *pp=='=';pp++);
if(*pp=='\"')
{
for(++pp;*pp!='\"';pp++)
{
if(*pp=='\\') pp++;
else *cp++ = *pp;
}
*cp='\0';
/* fprintf(stderr,"get_sub: quote (%s)\n",to); */
pp++;
}
else
{
for(hold=pp;*hold && *hold!=',';hold++);
if(*hold)
{
*hold = '\0';
hold++;
/* fprintf(stderr,"get_sub: regular (%s)\n",pp); */
}
strcpy(to,pp);
pp=hold;
}
return pp;
}