#ifndef __LCLINT__ /* * Copyright 1996, 1997, 1998, 1999 by Daniel B. Suthers, * Pleasanton Ca. 94588 USA * E-MAIL dbs@tanj.com * * * You may freely copy, use, and distribute this software, * in whole or in part, subject to the following restrictions: * * 1) You may not charge money for it. * 2) You may not remove or alter this copyright notice. * 3) You may not claim you wrote it. * 4) If you make improvements (or other changes), you are requested * to send them to me, so there's a focal point for distributing * improved versions. * */ #endif /* start by validating the file, then go back and load it. */ #include #include #include #include #include #include #include #include "eeprom.h" #include "x10.h" #include #define MAXTYPES 4 #define MAXCMDS 7 extern int char2hc(); extern unsigned int cm11bitmap(); extern struct x10_mod * xmod_lookup(); extern unsigned char cm11map[]; extern int verbose; unsigned int jul(); int showmem; unsigned char prommap[PROMSIZE]; extern char macroxref[]; /* name of macro cross reference file */ /* These values match the labels used in the sched config file. They must * match the order of the enum below it. */ char types[MAXTYPES][9] = { "daily", "trigger", "timer", "macro" }; enum etypes {daily, trigger_t, timer, macro }; /* These values match the code used by the CM11A. This lets us write them * directly to memory without translation. It also allows us to test, as in * if( y == dim ). Used in conjunction with the commands array we can easily * compare ascii to valid values and then translate them to CM11 commands. */ char commands[MAXCMDS][9]={ "dummy", "dummy", "on", "off", "dim", "bright", "wait" }; enum ecmd {dummy, dummy1, on, off, dim, bright, wait}; struct macro_loc { char label[32]; int promloc; }; struct macro_loc macro_xref[127]; /* Hold a maximum of 127 macros */ extern int usage(); extern int getunits(), error(); int validate_sched(); int find_macro(); int find_all_macros(); /* * This is the routine called by main when the argument 'macro' is given * It parses a config file, erases the CM11's eeprom, then loads the * new macros and timers. */ void c_load_macro(argc, argv) int argc; char *argv[]; { FILE *infile; char *configfile, *home; char line[256]; struct stat file_buf; int rtn, x; unsigned char emptyprom[PROMSIZE]; char RCSID[]= "@(#) $Id: parse_sched.c,v 1.12 2003/03/17 01:40:32 dbs Exp dbs $"; display(RCSID); rtn = 0; for( x = 0; x < PROMSIZE; x++) prommap[x] = (unsigned char)0xff; if( argc > 2) { if( (strcmp("validate", argv[2]) == 0 ) || (strcmp("check", argv[2]) == 0 ) ) { showmem = 1; } else { (void)usage("invalid option used with macro"); /* usage exits */ } } /* Look for a schedule file in the environment */ configfile = getenv("X10SCHED"); if (configfile == NULL) { /* No schedule in env. Try the default in the home directory */ home=getenv("HOME"); if( home == NULL ) { home = "."; } (void) strcat(strcpy(line, home), "/.x10sched.conf"); configfile = line; } if( stat(configfile,&file_buf) != 0 ) { strcpy(line, "/etc/x10sched.conf"); configfile = line; } if( verbose ) fprintf(stderr, "Macro schedule file=%s\n", configfile); /* find and open the file */ infile=fopen( configfile, "r"); if( infile != NULL ) rtn=validate_sched(infile); else /* error exits */ (void) error("could not open the schedule configuration file"); if(verbose) fprintf(stderr, "The load returned %d\n", rtn); if( rtn != 0 ) (void) error("Bad entry in the schedule configuration file"); /* We have a valid map of where to put things in the prom. */ /* Before we upload it to the CM11, Zero out the initiators, */ /* set up the empty virtual prom */ emptyprom[0] = (unsigned char)0; emptyprom[1] = (unsigned char)3; for( x=2; x < 16; x++) emptyprom[x] = (unsigned char)0xff; if( verbose || (showmem && (getenv("X10DEBUG") != NULL) ) ) { fprintf(stdout, "This data will be loaded to the CM11 eeprom.\n"); for(rtn=0; rtn < PROMSIZE ; rtn++) { fprintf(stdout, "Byte %4d(0x%3x) = %x \n",(unsigned int)rtn, rtn, (unsigned int) prommap[rtn]); } } /* erase the old eeprom header information */ if( ! showmem ) { if ( sendpacket(0 , emptyprom) < 0 ) { (void)error("load_macro() failed to erase initiator block"); /* error() exits */ } } /* Copy the data from the array 'prommap' to the CM11A */ /* Load backwards * This is so the timers at the end of the eeprom are there when * the initiators are loaded at the start of the eeprom */ if( ! showmem ) { fprintf(stdout, "Loading all %d blocks of data. Please stand by.\n", PROMSIZE / 16 ); } if( ! showmem ) { for(x=(PROMSIZE / 16) -1 ;x >= 0; x--) { if (verbose) fprintf( stderr, "Loading %d\n", x * 16 ); if ( sendpacket((x * 16) , prommap+(x*16)) < 0 ) { char tmpstr[100]; sprintf( tmpstr, "load failed to write block %d\n", x); error(tmpstr); } fputc( '.', stdout); fflush(stdout); } } fputc('\n', stdout); } #define MAXTIMERS 128 /* This function parses the .x10sched.conf file and then loads the * data into the virtual prom area. The calling function is responsible * for actually downloading the data to the CM11. */ int validate_sched(sch_file) FILE *sch_file; { char inbuf[256], *pinbuf; char *ptmp; /* general use within 24 lines of code */ char trigger[11], onoff[4], target[128], target1[128], cmd[9], cmd2[7]; char macro_label[32], smacro[32], emacro[32]; char cmds[128]; /* string of semicolon separated commands */ char type[12]; /* record type */ char tmpbuf[81]; /* general use within 24 lines of code */ FILE *fmacroxref; int x, y, lines, count, unit, onflag; int tmpi; /* general use within 24 lines of code */ int hr, mi, delay; int m_count; /* macro counter */ int ti_count; /* timer counter */ int t_length; /* length of the timer section */ int tr_count; /* trigger (initiator) counter */ int tr_length; /* Length of the trigger section */ int tom; /* top of memory, i.e. 1024 */ char dow[9]; /* store day of week as letters (smt.tfs) */ unsigned char dow_bitmap; int macrodata_start; unsigned int bits; char startdate[10], enddate[10], stod[6], etod[6] ; char startmo[4], startda[4], endmo[4], endda[4]; struct macrolinkl *lnk; struct timerinit tini_a[MAXTIMERS]; /* temp storage forTimer entries */ struct macroinit ini_a[128]; struct basicmacrodata data_a[128]; struct x10_mod *mod; lines=0; m_count = 0; ti_count=0; tr_count=0; lnk=(struct macrolinkl *)NULL; /* initialize the virtual prom to 0 */ for( x=0; x < sizeof(prommap) -1; x++) prommap[x] = (unsigned char)0xff; /* find all the macros in the file */ if( (tmpi=find_all_macros(sch_file)) < 0 ) { sprintf( tmpbuf, "Some invalid macro definitions at line %d", tmpi); error(tmpbuf); } rewind(sch_file); /* Start parsing the file */ while(fgets(inbuf, 160, sch_file) != NULL ) { inbuf[161] =(char) NULL; /* make sure the string is NULL terminated*/ lines++; /* skip leading white space */ for( x = 0; x < (int)strlen(inbuf); x++) if( !isspace((unsigned char)inbuf[x]) ) break; pinbuf = &inbuf[x]; /* skip comments */ if( *pinbuf == '#') continue; /* convert the whole line to lower case */ for( x=0; x < (int)strlen(inbuf); x++) inbuf[x] = tolower((unsigned char)inbuf[x]); /* grab the first token */ count=sscanf(pinbuf, "%10s ", type); if( count != 1 ) /* skip on errors and blank lines */ continue; /* skip blank lines */ if(type[0] == '\0' || (type[0]) == (char)0x0a ) continue; /* validate the first field. Find the index into the types array */ for(x=0; x < MAXTYPES; x++) { if( strcmp(type, types[x]) == 0 ) break; } if( x == MAXTYPES ) { fprintf(stderr, "The first field of line %d of the schedule file is invalid\n", lines); return(-1); } /* now evalaute the rest of the line. x == the enum matching the type*/ switch(x) { case timer: { /* at this point we have a daily timer line to parse. */ /* type timer dow (bitmap of days of the week smtwtfs) startdate (month/day) enddate (month/day) stod (hh:mm) (start time of day) etod (hh:mm) (end time of day) smacro (a macro to execute at start time) emacro (a macro to execute at end time) */ count=sscanf(pinbuf,"%10s %8s %5s-%5s %5s %5s %31s %31s", type, dow, startdate, enddate, stod, etod, smacro, emacro ); if( count != 8 ) { fprintf(stderr, "Invalid timer definition (bad count) on line %d\n", lines); return(-1); } if( strlen(dow) != 7) { fprintf(stderr, "Day of week flags should be 7 letters in line %d\n", lines); return(-1); } if( sscanf(startdate, "%[^/]/%s", startmo, startda) != 2 ) { fprintf(stderr, "Invalid timer start date on line %d\n", lines); return(-1); } if( sscanf(enddate, "%[^/]/%s", endmo, endda) != 2 ) { fprintf(stderr, "Invalid timer end date on line %d\n", lines); return(-1); } /* Time to store the timer information in the array */ tini_a[ti_count].start_jul = jul(startmo,startda) ; if( tini_a[ti_count].start_jul < 0 ) { fprintf(stderr, "Invalid start date on line %d\n", lines); return(-1); } if( verbose ) fprintf(stdout, "Start julian date = %u\n", tini_a[ti_count].start_jul); tini_a[ti_count].hi_startjul = tini_a[ti_count].start_jul / 256; tini_a[ti_count].start_jul %= 256; dow_bitmap = (unsigned char)0; for(x=0; x < 7; x++) { if( isalpha((int)dow[x]) ) dow_bitmap |= (1 << x); } tini_a[ti_count].dow = dow_bitmap; if( (tini_a[ti_count].stop_jul = jul(endmo,endda) ) < 0 ) { fprintf(stderr, "Invalid end date on line %d\n", lines); return(-1); } tini_a[ti_count].hi_stopjul = tini_a[ti_count].stop_jul / 256; tini_a[ti_count].stop_jul %= 256; if( sscanf(stod, "%d:%d", &hr, &mi) != 2 ) { fprintf(stderr, "Invalid start time on line %d. Should be hh:mm where hh is 0-23, mm is 0-59.\n", lines); return(-1); } mi += (60 * hr); if( mi > 1439 ) { fprintf(stderr, "Invalid start time on line %d. Should be hh:mm where hh is 0-23, mm is 0-59.\n", lines); return(-1); } tini_a[ti_count].start_time = mi / 120; tini_a[ti_count].start_mins = mi % 120; /* Store the macro indexes into the macro_xref array * for later. We don't have the prom addresses assigned yet. */ tini_a[ti_count].low_start_macro = find_macro(smacro); tini_a[ti_count].low_stop_macro = find_macro(emacro); if( sscanf(etod, "%d:%d", &hr, &mi) != 2 ) { fprintf(stderr, "Invalid end time on line %d. Should be hh:mm where hh is 0-23, mm is 0-59.\n", lines); return(-1); } mi += (60 * hr); if( mi > 1439 ) { fprintf(stderr, "Invalid end time on line %d. Should be hh:mm where hh is 0-23, mm is 0-59.\n", lines); return(-1); } tini_a[ti_count].stop_time = mi / 120; tini_a[ti_count].stop_mins = mi % 120; ti_count++; if (ti_count >= MAXTIMERS ) { puts("Maximum timers have been exceeded."); return(-1); } } /* end of timer */ break; case trigger_t : { /* at this point we have a trigger line to parse. */ count=sscanf(pinbuf,"%10s %3s %4s %31s", type, trigger, onoff, macro_label); /* type (macro) trigger (HcUnit) onoff (on or off) macro_label (string representing a macro name) */ if( count != 4 ) { fprintf(stderr, "Invalid trigger (too many fields) on line %d\n", lines); return(-1); } /* trigger should be house code + number * We could use aliases, but that would complicate things * by quite a bit (multiple addresses, for one) */ if( ! ((trigger[0] >= 'a') && trigger[0] <= 'p') ) { fprintf(stderr, "Invalid trigger house code on line %d\n", lines); return(-1); } unit=atoi(trigger+1); if( ! (unit > 0) && (unit < 17) ) { fprintf(stderr, "Invalid trigger unit number on line %d\n", lines); return(-1); } if( strcmp(onoff, "on") == 0) onflag = 1; else if( strcmp(onoff, "off") == 0) onflag = 0; else { fprintf(stderr, "Invalid trigger command on line %d\n", lines); return(-1); } macro_label[31] = (char)NULL; /* make sure it's terminated */ if( find_macro(macro_label) < 0) { fprintf(stderr, "Invalid macro label value on line %d\n", lines); return(-1); } /* we have the data, Let's save it. */ /* first the initiator */ ini_a[tr_count].hc = char2hc(trigger[0]); ini_a[tr_count].dev = cm11map[unit -1] ; ini_a[tr_count].onoff = onflag; ini_a[tr_count].macro_pointer = find_macro(macro_label); if( ini_a[tr_count].macro_pointer < 0 ) { fprintf(stderr, "Invalid macro label value on line %d\n", lines); return(-1); } tr_count ++; } break; case macro: { /* at this point we have a macro line to parse. */ count=sscanf(pinbuf,"%10s %31s %d %126[0-9a-z; ,\t-]", type, macro_label, &delay, cmds); /* type (macro) macro_label (text string to identify the macro) delay (minutes < 200) target (HcUnit , I E A1) cmds (a list of commands and arguments, usually pairs ( on and off, e.g. on d1;off d3,d4,d5) but sometimes triplets (dim and bright) */ if( count != 4 ) { fprintf(stderr, "Invalid Macro on line %d (wrong number of fields)\n", lines); return(-1); } m_count=find_macro(macro_label); if( delay < 0 || delay > 240) { fprintf(stderr, "Invalid Macro delay on line %d. It should be from 0 to 240.\n", lines); return(-1); } /* look at the commands */ data_a[m_count].memsize = 3; /* delay, size and terminator*/ /* break down the commands into discreet groups so we can * figure out how many sub parts are involved */ ptmp = cmds; data_a[m_count].size = (unsigned char)1; while( *ptmp != (char) NULL ) { if( *(ptmp++) == ';') data_a[m_count].size ++; } /* for each macro sub command set, get the data, then parse it*/ ptmp = cmds; for( x=0; x < data_a[m_count].size ; x++ ) { /* set up the link list. Start with the .lnk member of * the data_a structure, allocating cleared memory as * necessary. */ if( x == 0 ) { data_a[m_count].lnk = calloc(1, sizeof(struct macrolinkl)); lnk = data_a[m_count].lnk; } else /* add all links after the first to the lnk pointer*/ { lnk->lnk = calloc(1, sizeof(struct macrolinkl)); lnk = lnk->lnk; } if (lnk == (struct macrolinkl *)NULL ) error("Could not allocate enough memory"); /* grab the next set of command/hc+unit pairs and * parse it. This may be the first or the 100th. */ sscanf( ptmp, "%s %[^;];", cmd, target); ptmp = index(ptmp, ';'); if( ptmp != NULL && *ptmp == ';') ptmp++; /* make sure the command is valid */ for( y=0; y < MAXCMDS; y++) { if(strcmp(commands[y], cmd) == 0) break; } switch(y) { case on: case off: lnk->fc=y; data_a[m_count].memsize +=3; break; case wait: continue; case dim : case bright : lnk->fc=y; data_a[m_count].memsize +=4; if( sscanf(target, "%127s %6s", target1, cmd2) != 2) { fprintf(stderr, "incomplete dim/bright in macro on line %d\n", lines); return(-1); } lnk->dim_value=atoi(cmd2); strcpy(target,target1); if( lnk->dim_value > 22 || lnk->dim_value < 0 ) { fprintf(stderr, "Invalid dim value in macro on line %d\n", lines); return(-1); } break; case MAXCMDS : { fprintf(stderr, "Invalid command in macro on line %d\n", lines); return(-1); } } mod = xmod_lookup(target); if ( mod != NULL ) { lnk->hc=char2hc(mod->hc); /* getunits returns a simple bit map of units where * bit 0 = unit */ bits=getunits(mod->un) ; } else { lnk->hc=char2hc(target[0]); /* getunits returns a simple bit map of units where * bit 0 = unit */ bits=getunits(target+1) ; } /* cm11bitmap makes it a cm11 style bitmap */ lnk->device_bm= cm11bitmap(bits) ; } /* end of for loop that parses cmds string */ if( x != data_a[m_count].size ) { error("The submacro count did not match the number of delimiters (;)"); } /* we have the data, Let's save it to the array. */ /* Now the data */ data_a[m_count].delay = delay; /* data_a[m_count].size=1; */ /* set in scan routine above */ } /* end of macro case */ break; } } /* end of while loop */ /* At this point; 1) figure out the length of the timer initators 2) Figure out the length of the macro initiators 3) Fill in the prommap array. (offset, timers, macro init, macro data). */ /* Length of timers. Each timer is 9 bytes long ; * there is a 0xff flag on the end. */ t_length = ((9 * ti_count)+1); tr_length= (3 * tr_count); macrodata_start=2+t_length+tr_length+1; /* Macro initiator offset is 2 + t_length (offset + tlength) */ /* Macro offset is 2 + timer length ) */ prommap[0]=(2+t_length) / 256; /* high byte of addr */ prommap[1]=(2+t_length) % 256; /* low byte of addr */ prommap[1+t_length] = 0xff; /* end of timers marker */ /* open the crossreference file for storing macro locations */ fmacroxref = NULL; if( (macroxref[0] != 0) && (showmem == 0) ) { fmacroxref = fopen( macroxref, "w" ); if( fmacroxref == NULL ) { perror("Error opening macro cross reference file"); error("Program exiting"); } } /* make top of memory counter zero relative */ tom=PROMSIZE -1; /* The macros start at the top of memory (TOM)and grow towards the begining. * This allows simpler testing for memory exhaust. It also allows us to * do the macros first without having to precalculate the memory used * by the timers and macro triggers. */ /* for each possible macro, insert the data into the virtual prom.*/ for(x=0; x < 127; x++) { if( macro_xref[x].label[0] == (char)NULL ) /* no label = end of macros*/ break; /* insert the data associated with the macro header */ tom -= data_a[x].memsize; /* start the proper distance from the end */ /* test for memory exhaust */ if( tom <= macrodata_start) error("Too many events / macros. Ran out of memory."); /* save the memory location for the timer and trigger routines */ macro_xref[x].promloc = tom; /* macro hdr */ prommap[tom]=data_a[x].delay; prommap[tom+1]=data_a[x].size; lnk = data_a[x].lnk; /* next insertion is after the macro header. Use a temp index */ tmpi=tom+2; /* for each command or subcommand, insert the data */ for( y = 0; y < data_a[x].size; y++) { /* common to on/off and dim are the hc, fc and bitmap */ prommap[tmpi++]=(char)(lnk->hc << 4) | lnk->fc ; prommap[tmpi++]= ((lnk->device_bm & 0xff00) >> 8) ; /* low */ prommap[tmpi++]=(unsigned char)lnk->device_bm & 0x00ff; /* hi */ /* if it's a bright/dim, add that value */ if( lnk->fc == bright || lnk->fc == dim ) prommap[tmpi++]=lnk->dim_value ; /* move the link pointer for the next iteration */ lnk = lnk->lnk; } prommap[tmpi++]=0; /* terminator of the macro */ /* common to all macros : tell the user where things are */ fprintf(stdout, "Macro #%d, %s written at %d (0x%x) \n", x, macro_xref[x].label, tom, tom); if( fmacroxref != NULL ) { fprintf( fmacroxref, "%d %s\n", tom, macro_xref[x].label); } } if( fmacroxref != NULL ) fclose(fmacroxref); /* Timers... */ /* These values are straight off the protocol.txt document. * Each timer is 9 bytes long. The initial 2 is added to get past * the first two bytes of memory that point to the start of the * macro triggers (initiators) */ /* For each timer we found... */ for(x=0; x < ti_count; x++) { if( 2+(9*x)+9 >= PROMSIZE ) error("Too many events / macros. Ran out of memory."); prommap[2+(9*x)+0] =(tini_a[x].dow & 0xff); prommap[2+(9*x)+1] = tini_a[x].start_jul; prommap[2+(9*x)+2] =tini_a[x].stop_jul; prommap[2+(9*x)+3] =((tini_a[x].start_time & 0x00f) << 4) ; prommap[2+(9*x)+3]|= tini_a[x].stop_time; prommap[2+(9*x)+4] = (tini_a[x].hi_startjul << 7); prommap[2+(9*x)+4]|= tini_a[x].start_mins ; prommap[2+(9*x)+5] = (tini_a[x].hi_stopjul << 7); prommap[2+(9*x)+5]|= (tini_a[x].stop_mins); /* high nibble of start macro ptr and stop macro ptr addresses */ prommap[2+(9*x)+6] = (macro_xref[tini_a[x].low_start_macro].promloc / 256 ) << 4; prommap[2+(9*x)+6]|= (macro_xref[tini_a[x].low_stop_macro].promloc) / 256; /* low byte of start macro ptr address */ prommap[2+(9*x)+7] = (macro_xref[tini_a[x].low_start_macro].promloc % 256); /* low byte of stop macro ptr address */ prommap[2+(9*x)+8] = (macro_xref[tini_a[x].low_stop_macro].promloc) % 256; } /* Now we can write the triggers (macro initiators). * They are always 3 bytes. They follow imediately after the timers. * The layout is straight from the protocol.txt file */ /* for each trigger we found */ for(x=0; x < tr_count; x++) { int start; start=(t_length+3)+(x*3)-1; /* the starting point for THIS trigger */ if( start >= PROMSIZE ) error("Too many events / macros. Ran out of memory."); prommap[start] = (ini_a[x].hc << 4) | ini_a[x].dev; prommap[start+1]=(ini_a[x].onoff << 7) | ( (macro_xref[ini_a[x].macro_pointer].promloc & 0xff00 )>>8) ; prommap[start+2]=(macro_xref[ini_a[x].macro_pointer].promloc & 0x00ff ); prommap[start+3]=0xff; /* just in case this is the last */ } return(0); } /* end of validate_sched */ /* return a julian date in the range of 0 to 364 (or 365 for leap year) */ unsigned int jul(amo, ada ) char *amo; /* ascii month */ char *ada; /* ascii day */ { unsigned int julian; int mo, da; time_t clk; struct tm *tm_str; static int months[] = { 31, /* Jan */ 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 , 31} /* Jan */; mo = atoi(amo); da = atoi(ada); /* make it 0 relative like the array, instead of 1 relative like * the calendar */ mo-- ; /* get leap year info */ time(&clk); tm_str = localtime(&clk); if( tm_str->tm_year % 4 == 0 ) /* Leap Year!!! Not y3k compliant */ { months[1] = 29; } if( (mo > 12) || (mo < 0)) { fprintf(stderr, "Month out of range\n"); return (-1); } if((da > months[mo]) || (da < 1)) { fprintf(stderr, "Day out of range\n"); return (-1); } julian = da; while( --mo >= 0 ) { julian += months[mo]; } /* Make it 0 relative (0-364 for normal year, 0-365 on leap year) */ julian-- ; return(julian); } /* Return the macro index for the given label */ int find_macro(label) char *label; { int found; int x; found = -1; for( x=0; x< 127;x++) { if( strncmp(label, macro_xref[x].label, 33) == 0 ) { found = x; } } return(found); } /* Search through the schedule file for any line that starts with * the word macro. When found, duplicates will be spotted. If all * looks good, it's stored in the array. */ int find_all_macros(infile) FILE *infile; { int x, count, lines; char inbuf[81]; char type[11]; char label[32]; char *pinbuf; count = 0; lines = 0; x = 0; /* Start parsing the file */ while(fgets(inbuf, 80, infile) != NULL ) { inbuf[80] =(char) NULL; /* make sure the string is NULL terminated*/ lines++; /* skip leading white space */ for( x = 0; x < strlen(inbuf); x++) if( !isspace((int)inbuf[x]) ) break; /* use a pointer to access start of data */ pinbuf = &inbuf[x]; /* skip comments */ if( *pinbuf == '#') continue; /* skip blank lines */ if( (*pinbuf == '\0' ) || (*pinbuf == 0x0a ) ) continue; /* convert the whole line to lower case */ for( x=0; x < strlen(inbuf); x++) inbuf[x] = tolower(inbuf[x]); /* grab the first token */ x=sscanf(pinbuf, "%10s %31s ", type, label); if( x != 2 ) /* skip on errors and blank lines */ continue; /* make sure they are null terminated */ type[9] = (char) NULL; label[31] = (char) NULL; if( strcmp(type, "macro") == 0 ) { if( count == 126 ) { fprintf(stderr, "Too many macro labels found.\n"); return(-1); } for(x=0; x < 127; x++) { if( macro_xref[x].label[0] == '\0') break; if( strcmp(label, macro_xref[x].label) == 0 ) { fprintf(stderr, "Duplicate macro labels found.\n"); return(-1); } } /* This is a good one. Store it */ strcpy(macro_xref[count].label, label); count++; } } return(lines); } char *storedmacro(location) int location; { FILE *fmacroxref; char line[81]; static char label[81]; int storedloc; if( macroxref[0] == (char) NULL) { return(""); } else { fmacroxref = fopen(macroxref, "r"); if( fmacroxref == NULL ) return("Trouble opening macro cross reference file\n"); } clearerr(fmacroxref); for (;;) { line [0] = '\0'; fgets(line, sizeof(line), fmacroxref); line[sizeof(line) -1] = (char) NULL; if ((line[0] == '\0') || (line[0] == 0x0a) ) { if( feof(fmacroxref) != 0 ) /* end of file */ { break; } else { continue; /* blank line */ } } if( sscanf(line, "%i %80s", &storedloc, label) != 2 ) return("macro file error"); label[sizeof(label) -1] = (char)NULL; if( storedloc == location ) break; } return(label); }