/* @(#)parse_schema.c 2.1 93/05/18 */ /******************************************************************************/ /* function : parse_schema */ /* */ /* subsystem : chgen */ /* */ /* input : */ /* */ /* output : */ /* */ /* returns : void, exits on fatal schema errors. */ /* */ /* author : Steve Smith / Craig Smith */ /* */ /* created : July, 1991 */ /* */ /* revisions : 93s523: Added code to incorporate integer keys: */ /* table abbrev size must match hcg_abbr_size, commands are */ /* modified to work with abbrev's of size hcg_abbr_size. */ /* */ /* description : This routine will read the schema file line at a time, pars- */ /* ing table and field data, and loading it into memory linked */ /* lists of structures. The tt linked list holds table-level */ /* data, such as name, abbrev, etc... Within each tt list elt */ /* is another linked list, ta, which holds field-level data. */ /* The parser itself is implemented as a simple state machine */ /* with 3 states. In the NULL State, the file read is between */ /* tables. Comments between tables are ignored. When a new */ /* tables definition starts, its data is parsed, a new tt */ /* list element is allocated and filled in, and the state is */ /* changed to STARTED. In this state, the only valid item to */ /* be parsed is the tables starting "{" symbol, and the state */ /* is then changed to WITHIN. In this state individual field */ /* definitions are parsed. The state is changed back to NULL */ /* when the tables closing "}" is parsed. As each field is */ /* found, a new ta list element is allocated, filled in, and */ /* added to the current tt elements ta list. Within each parse */ /* phase, basic syntactic checks are applied, and errors may */ /* be issued. When foreign keys are found, the parent/child */ /* and child/parent relationship is logged to the pc and cp */ /* tables respectively via the add_pc_cp_entries() routine. */ /* In addition, when primary keys are found, appropriate entries*/ /* are added to the TINDEX array, which is used by the access- */ /* macro generation routines. */ /* */ /******************************************************************************/ /*****************************************************************************/ /* CHGEN V9.0 94sgen - mcook, hsano, jhyun */ /* */ /* */ /* Basic enhancements include the ability to read in both a gendb and a */ /* schema file, by specifying a command line switch. */ /* */ /* Revision history: */ /* */ /* 5/18/94 - mcook - Changed around the header to the metaschema.dat file */ /* to reflect an actual table type. */ /* Changed fkey naming conventions to TTid, TTid2, */ /* TTid3... instead of TTid, TT02, TT03... */ /* Changed CHGEN-F-NOPATHS to CHGEN-W-NOPATHS (i.e. from*/ /* a fatal error to a warning. */ /* */ /* 5/16/94 - mcook - Added error checking: */ /* CHGEN-F-ERRNOPARENT - Searches for the parent */ /* referenced in a relation and does not come up with a */ /* matching parent, (this limits the schema to not allow*/ /* forward references.) */ /* genDB parsing: Parsed out parent references. */ /* */ /* 5/15/94 - mcook - Bug fixes: Fixed the case of multiple references to */ /* the same primary key by adding table name plus a */ /* number which gets incremented per reference. */ /* */ /* 5/13/94 - mcook - Added the filename to the datafile, and changed the */ /* title of the datafile to reflect where it is coming */ /* from. Tried to add date and time stamping, but it */ /* always returned the same (wrong) date and time. */ /* */ /* 5/12/94 - mcook - Added error checking: */ /* CHGEN-F-NOPATHS - Signifying that the genDB schema */ /* line "create path" is not supported in this */ /* version if chgen. */ /* CHGEN-F-ERROPENMSFILE - Signifying that the datafile */ /* could not be opened properly. */ /* */ /* 5/10/94 - mcook - Added the function Output_MetaSchema(), which reads */ /* the TT and TA tables and outputs them to a file if */ /* the command line qualifier "-datafile=" was */ /* specified with a filename. */ /* */ /* 5/04/94 - mcook - genDB parsing: Created a primary_key line, which is */ /* the first line in a chgen schema table--but does not */ /* exist in the current genDB schema, from the table */ /* name and abbreviation. */ /* */ /* 5/01/94 - mcook - Added the ability to read in either a chgen or a */ /* gendb schema file by specifying a command line */ /* switch "-gendbschema" */ /* */ /* 4/26/94 - mcook - genDB parsing: Parsed out "create table" to */ /* determine start of a new table. */ /*****************************************************************************/ #include #include #include /* string.h include added by genlog team 96s523 to suppress error message */ #include #include "chgen_define.h" #include "chgen_externs.h" #include "prototypes.h" int cfkey; char lastfkey[NAMELENGTH]; void parse_schema() { if (cli_gendbschema) parse_schema_gendb(); else if (!inputDatFile) parse_schema_chgen(); else parse_msdat_chgen(); } void parse_msdat_chgen() { int idx; char is_key[10]; int ifound = FALSE; int i; /* for loop counter */ sv = NULL; tt = NULL; tt_curr = NULL; tab_seq_no = 0 ; num_tables = 0; strcpy(tab_name,""); strcpy(tab_abbr,""); strcpy(field_name,""); strcpy(is_key,"0"); comment[0] = '\0'; read_next(); while ( !feof ( schtxt_fp ) ) { idx = 0; hcg_parse(buffer,temp_buffer,&idx); if ( (temp_buffer[0] == '\0') || (strncmp(temp_buffer,"/*",2) == 0) ) /* blank or comment line */ continue; ifound = TRUE; /* not a comment or a blank, therefore process the line */ /* check that the rest of the schemaNumber after the abbreviation is all numbers */ checkRange(&temp_buffer[hcg_abbr_size], HCG_KEY_SIZE-hcg_abbr_size, "0", "9"); if (strlen(temp_buffer) != HCG_KEY_SIZE) { printf("CHGEN-F-WRONGKEYSIZE, SV key %s is not %d characters long\n", temp_buffer,HCG_KEY_SIZE); exit(1); } wordToUpper(temp_buffer,hcg_abbr_size); /* convert first 2 (or 4) chars to upper*/ if (strncmp(temp_buffer,"SV",2) != 0) { /* the first line of the .msdat file should be of the form */ /* SV010001 PJ010001 schooldb.sch CHGEN v11.0 CHGEN */ /* or be a blank line or a comment */ /* there can only be one SV line in the .dat file */ if (sv) { printf("CHGEN-F-DUPLICATESV, SV key %s: SV cannot be defined twice\n", temp_buffer); exit(1); } sv = (struct sv_type*) malloc (sizeof (struct sv_type)); strcpy(sv->schemaNumber,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get the project name */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-PJNOTDEFINED, project number is missing from sv table\n"); exit(1); } wordToUpper(temp_buffer,hcg_abbr_size); /* convert first 2 (or 4) chars to upper*/ if (strncmp(temp_buffer,"PJ",2) != 0) { printf("CHGEN-F-PJNOTDEFINED, PJ number expected as second word in file. %s was found\n", temp_buffer); exit(1); } if (strlen(temp_buffer) != HCG_KEY_SIZE) { printf("CHGEN-F-WRONGKEYSIZE, PJ number key %s is not %d characters long\n", temp_buffer,HCG_KEY_SIZE); exit(1); } /* check that the rest of the projectNumber after the abbreviation is all numbers */ checkRange(&temp_buffer[hcg_abbr_size], HCG_KEY_SIZE-hcg_abbr_size, "0", "9"); /* this is a good project number. Therefore save it in the sv_type struct */ strcpy(sv->projectNumber,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get the schema name */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-SNAMENOTDEFINED, schema name is missing from sv table\n"); exit(1); } if (strlen(temp_buffer) > NAMELENGTH) { printf("CHGEN-F-NAMETOOLONG, schema name %s exceeds the maximum length of %d\n", temp_buffer,NAMELENGTH); exit(1); } /* this is a schema name. Therefore save it in the sv_type struct */ strcpy(sv->schemaName,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get the schema format */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-SVFORMATNOTDEFINED, schema format is missing from sv table\n"); exit(1); } if (strlen(temp_buffer) > NAMELENGTH) { printf("CHGEN-F-NAMETOOLONG, schema format %s exceeds the maximum length of %d\n", temp_buffer,NAMELENGTH); exit(1); } /* this is a schema format. Therefore save it in the sv_type struct */ strcpy(sv->schemaFormat,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get the schema version */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-SVVERNOTDEFINED, schema version is missing from sv table\n"); exit(1); } if (strlen(temp_buffer) > 2) { printf("CHGEN-F-NAMETOOLONG, schema version %s exceeds the maximum length of %d\n", temp_buffer,2); exit(1); } checkRange(temp_buffer, 2, "0", "9"); /* this is a schema version. Therefore save it in the sv_type struct */ sv->schemaVersion,atoi(temp_buffer)); read_next(); // read the next line } else if (strncmp(temp_buffer,"TT",2) != 0) { /* read and parse the TT table types */ if (strlen(temp_buffer) != HCG_KEY_SIZE) { printf("CHGEN-F-WRONGKEYSIZE, TTNumber %s is not %d characters long\n", temp_buffer,HCG_KEY_SIZE); exit(1); } /* check that the rest of the TTNumber after the abbreviation is all numbers */ checkRange(&temp_buffer[hcg_abbr_size], HCG_KEY_SIZE-hcg_abbr_size, "0", "9"); /******************************************/ /* Starting a new table, so add a node to */ /* the tt list. */ /******************************************/ tt_tmp = (struct tt_type*) malloc (sizeof (struct tt_type)); strcpy(tt_tmp->TTNumber,temp_buffer); tab_seq_no++; tt_tmp->next_ptr = NULL; tt_tmp->ta_ptr = NULL; /* check that there is not a duplicate TTNumber in the .dat file */ for (i = 0; i < num_tables; i++) { if (strcmp(sv->tt_ptr[i]->TTNumber, tt_tmp->TTNumber) == 0) { printf("CHGEN-F-DUPLICATETT, TTNumber %s is a duplicate\n", temp_buffer); exit(1); } } sv->tt_ptr[num_tables] = tt_tmp; num_tables++; if (tt == NULL) { tt = tt_tmp; tt_curr = tt_tmp; } else { tt_curr->next_ptr = tt_tmp; tt_curr = tt_curr->next_ptr; } /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get the schemaNumber */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-SVNOTDEFINED, schemaNumber is missing from tt table\n"); exit(1); } wordToUpper(temp_buffer,hcg_abbr_size); /* convert first 2 (or 4) chars to upper*/ if (strncmp(temp_buffer,"SV",2) != 0) { printf("CHGEN-F-SVNOTDEFINED, schemaNumber expected as second word in file. %s was found\n", temp_buffer); exit(1); } if (strlen(temp_buffer) != HCG_KEY_SIZE) { printf("CHGEN-F-WRONGKEYSIZE, schemaNumber %s is not %d characters long\n", temp_buffer,HCG_KEY_SIZE); exit(1); } /* check that the rest of the schemaNumber after the abbreviation is all numbers */ checkRange(&temp_buffer[hcg_abbr_size], HCG_KEY_SIZE-hcg_abbr_size, "0", "9"); /* this is a good schemaNumber. Therefore save it in the tt_type struct */ strcpy(tt_tmp->schemaNumber,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get TTAbbr */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-TTAbbrNOTDEFINED, TTAbbr is missing from tt table\n"); exit(1); } if (strlen(temp_buffer) > ABBREV_NAME_LENGTH) { printf("CHGEN-F-NAMETOOLONG TTAbbr %s is greater than %d characters long\n", temp_buffer,ABBREV_NAME_LENGTH); exit(1); } /* this is a good TTAbbr. Therefore save it in the tt_type struct */ strcpy(tt_tmp->TTAbbr,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get TableName[NAMELENGTH] */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-TABLENAMENOTDEFINED, TableName is missing from tt table\n"); exit(1); } if (strlen(temp_buffer) > NAMELENGTH) { printf("CHGEN-F-NAMETOOLONG Tablename %s is greater than %d characters long\n", temp_buffer,NAMELENGTH); exit(1); } /* this is a good TableName. Therefore save it in the tt_type struct */ strcpy(tt_tmp->TableName,tab_name); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get comment */ if (strncmp(temp_buffer,"/*",2) != 0) { printf("CHGEN-W-MISSINGCOMMENT, Comment is missing from tt table\n"); comment[0] = '\0'; } if (strlen(temp_buffer) > MAXCOMMENTLENGTH) { printf("CHGEN-F-COMMENTTOOLONG Comment is greater than %d characters long\n Comment:%s\n", MAXCOMMENTLENGTH,temp_buffer); exit(1); } /* this is a good comment. Therefore save it in the tt_type struct */ strcpy(tt_tmp->comment,comment); } // done with processing a TT line in a .dat file /*********************************************************************************/ else if (strncmp(temp_buffer,"TA",2) != 0) { /* read and parse the TA table types */ if (strlen(temp_buffer) != HCG_KEY_SIZE) { printf("CHGEN-F-WRONGKEYSIZE, TANumber %s is not %d characters long\n", temp_buffer,HCG_KEY_SIZE); exit(1); } /* check that the rest of the TANumber after the abbreviation is all numbers */ checkRange(&temp_buffer[hcg_abbr_size], HCG_KEY_SIZE-hcg_abbr_size, "0", "9"); /***********************************************/ /* Add this field name to the ta list. */ /***********************************************/ ta_tmp = (struct ta_type*) malloc (sizeof (struct ta_type)); strcpy(ta_tmp->TANumber,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get the TTNumber */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-TTNOTDEFINED, TTNumber is missing from ta table\n"); exit(1); } wordToUpper(temp_buffer,hcg_abbr_size); /* convert first 2 (or 4) chars to upper*/ if (strncmp(temp_buffer,"TT",2) != 0) { printf("CHGEN-F-TTNOTDEFINED, TTNumber expected as second word in file. %s was found\n", temp_buffer); exit(1); } if (strlen(temp_buffer) != HCG_KEY_SIZE) { printf("CHGEN-F-WRONGKEYSIZE, TTNumber %s is not %d characters long\n", temp_buffer,HCG_KEY_SIZE); exit(1); } /* check that the rest of the TTNumber after the abbreviation is all numbers */ checkRange(&temp_buffer[hcg_abbr_size], HCG_KEY_SIZE-hcg_abbr_size, "0", "9"); /* this is a good TTNumber. Therefore save it in the ta_type struct */ strcpy(ta_tmp->TTNumber,temp_buffer); /* verify that TTNumber is a valid table number */ /* and return tt_tmp as pointer to the matching TT */ if (tt_tmp = getTTfromTA(tt,ta_tmp) ) == NULL) { printf("CHGEN-F-MISSINGTT, TTNumber %s for TANumber %s is not in database\n", temp_buffer,ta_tmp-TANumber); exit(1); } /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get FieldName */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-FIELDNAMENOTDEFINED, FieldName is missing from ta table\n"); exit(1); } if (strlen(temp_buffer) > NAMELENGTH) { printf("CHGEN-F-NAMETOOLONG FieldName %s is greater than %d characters long\n", temp_buffer,NAMELENGTH); exit(1); } /* this is a good FieldName. Therefore save it in the ta_type struct */ strcpy(ta_tmp->FieldName,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get AltFieldName */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-FIELDNAMENOTDEFINED, AltFieldName is missing from ta table\n"); exit(1); } if (strlen(temp_buffer) > NAMELENGTH) { printf("CHGEN-F-NAMETOOLONG AltFieldName %s is greater than %d characters long\n", temp_buffer,NAMELENGTH); exit(1); } /* this is a good AltFieldName. Therefore save it in the ta_type struct */ strcpy(ta_tmp->AltFieldName,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get FieldType */ /* verify that the FieldType is one of the following: */ /* i2 or i4 */ /* f4 or f8 */ /* date */ /* cX or tX, where X is an integer from 1 to MAXCOMMENTLENGTH */ switch (temp_buffer[0]) { case 'i': if ((strcmp(temp_buffer+1,"2") != 0) && (strcmp(temp_buffer+1,"4") != 0)) { if (!cli_quiet) printf("CHGEN-W-BADINTTYPE, integer data-type %s for field %s is invalid, value types are i2 and i4\n", temp_buffer,ta_tmp->FieldName); } break; case 'f': if ((strcmp(temp_buffer+1,"4") != 0) && (strcmp(temp_buffer+1,"8") != 0)) { if (!cli_quiet) printf("CHGEN-W-BADFLOATTYPE, float data-type %s for field %s is invalid, value types are f4 and f8\n", temp_buffer,ta_tmp->FieldName); } break; case 'd': if (strcmp(temp_buffer+1,"ate") != 0) { printf("CHGEN-F-BADDATE, data-type %s for field %s is not a valid date type\n", temp_buffer,ta_tmp->FieldName); exit(1); } break; case 'c': case 't': if ( (atoi(temp_buffer+1) <= 0) || (atoi(temp_buffer+1) > MAXCOMMENTLENGTH) ) { printf("CHGEN-F-CHARTEXT, char/text data-type %s for field %s has an invalid length specifier\n", temp_buffer,ta_tmp->FieldName); exit(1); } break; default: { printf("CHGEN-F-BADTYPE, unsupported data-type %s specified for field %s\n", temp_buffer,ta_tmp->FieldName); exit(1); } } /* switch */ /* this is a good FieldType. Therefore save it in the ta_type struct */ strcpy(ta_tmp->FieldType,temp_buffer); /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get Iskey */ if (temp_buffer[0] == '\0') /* no more words in line! */ { printf("CHGEN-F-ISKEYNOTDEFINED, IsKey is missing from ta table\n"); exit(1); } if ((strcmp(temp_buffer,"0") != 0) && (strcmp(temp_buffer,"1") != 0) && (strcmp(temp_buffer,"s") != 0) && (strcmp(temp_buffer,"-1") != 0)) { printf("CHGEN-F-BADISKEY, 'is-key' field %s for field %s must be 0, 1, -1 or s\n", temp_buffer,ta_tmp->FieldName); exit(1); } ta_tmp->IsKey = (temp_buffer[0] != '0'); ta_tmp->HasBp = (temp_buffer[0] == '1') || (temp_buffer[0] == 's'); ta_tmp->Singleton = (temp_buffer[0] == 's'); ta_tmp->next_ptr = NULL; if (tt_tmp->ta_ptr == NULL) { tt_tmp->ta_ptr = ta_tmp; ta_curr = ta_tmp; } else { ta_curr->next_ptr = ta_tmp; ta_curr = ta_curr->next_ptr; } if ((ta_tmp->IsKey[0] != '0') && (strcmp(ta_tmp->FieldType,"c8") != 0)) { if (tt_tmp->ta_ptr == NULL) printf("CHGEN-F-KEYMUSTBEC8, primary key field %s must be of type c8 \n", ta_tmp->FieldName); else printf("CHGEN-F-KEYMUSTBEC8, foreign key field %s must be of type c8 \n", ta_tmp->FieldName); exit(1); } if (tt_tmp->ta_ptr == NULL) /* first field, must be key and first 2 abbrev==tab_abbrev */ { if (is_key[0] != '1') { printf("CHGEN-F-FIRSTMUSTBEKEY, first field %s must have a 'is-key' value of 1\n", ta_tmp->FieldName); exit(1); } if (strncmp(ta_tmp->FieldName,tt_tmp->TTabbr,hcg_abbr_size) != 0) { printf("CHGEN-F-PKEYABBREVNOMATCH, first %d characters of primary key field %s ", hcg_abbr_size, ta_tmp->FieldName); printf(" must equal the table abbreviation %s\n", tt_tmp->TTabbr); exit(1); } /* get the hash_value for the tab_abbrev_name*/ /* and put corresponding tab_seq_no into */ /* field_list */ TXindex = encoding(tt_tmp->TTabbr); /* fill up TINDEX */ TINDEX[TXindex].TXindex = TXindex ; TINDEX[TXindex].TTindex = tab_seq_no; strcpy(TINDEX[TXindex].TTabbr, tt_tmp->TTabbr); } /* first field, must be pkey */ else if (is_key[0] != '0') /* must be a foreign key */ { add_pc_cp_entries(ta_tmp->FieldName,tt_curr->ta_ptr->FieldName,is_key); } /*********************************************************************************/ hcg_parse(buffer,temp_buffer,&idx); /* get comment */ if (strncmp(temp_buffer,"/*",2) != 0) { printf("CHGEN-W-MISSINGCOMMENT, Comment is missing from tt table\n"); comment[0] = '\0'; } if (strlen(temp_buffer) > MAXCOMMENTLENGTH) { printf("CHGEN-F-COMMENTTOOLONG Comment is greater than %d characters long\n Comment:%s\n", MAXCOMMENTLENGTH,temp_buffer); exit(1); } /* this is a good comment. Therefore save it in the tt_type struct */ strcpy(ta_tmp->comment,comment); } // done with processing a TA line in a .dat file else { printf("CHGEN-F-ROWTYPENOTFOUND, Row type %s is not an SV, TT, or TA type\n" temp_buffer); exit(1); } } // end of while ( !feof ( schtxt_fp ) ) if ( !ifound ) { printf("CHGEN-F-FILEEMPTY, .msdat file is empty\n"); exit(1); } } /* end func */ /* this function converts the first numChar characters of a word to upper case */ void wordToUpper(char *word,int numChar) { int i; for(i=0; i < numChar; i++) word[i] = isupper(word[i]) ? word[i] : toupper(word[i]); } /* this function checks that a word is between a range of characters */ /* if not, it prints an error and exits */ void checkRange(char *word, int numChar, char low, char high) { int i; for (i=0; i high) || (word[i]] < low) ) { printf("CHGEN-F-CHECKRANGE, word %s has characters out of the range %c to %c\n", word, low, high); exit(1); } } } tt_type *getTTfromTA(tt,ta) tt_type *tt; ta_type *ta; { while (tt) { if (strcmp(tt->TTNumber, ta->TTNumber) == 0) return tt; tt = tt->next; } return tt; }