#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include /* chmod(2) */ #include #include #include #include #include /* strdup(3) */ #include /* basename(3) */ #include /* tolower(3) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define RLIST_ERRMSG_SIZE 4096 void rlist_error( const char *fmt, ... ) { va_list arg_ptr; char buf[RLIST_ERRMSG_SIZE]; char msg[RLIST_ERRMSG_SIZE]; char *format = "%s: %s\n"; va_start( arg_ptr, fmt ); vsnprintf( msg, RLIST_ERRMSG_SIZE, (const void *)fmt, arg_ptr ); va_end( arg_ptr ); /* Reset variable arguments. */ snprintf( buf, RLIST_ERRMSG_SIZE, format, "rlist", msg ); (void)write( STDERR_FILENO, buf, strlen( buf ) ); exit( 1 ); } rlist_errfunc rlist_fatal = rlist_error; struct dlist *config = NULL; static void *bcf = NULL; static size_t read_bcf( void ) { int fd; struct stat st; void *addr; fd = shm_open( CSVN_SHM_BCF, O_RDONLY, S_IRUSR | S_IWUSR ); if( fd == -1 ) { rlist_fatal( "Canot open SHM/%s data", "csvn.bcf" ); return 0; } if( !fstat( fd, (struct stat *)&st ) ) { addr = mmap( NULL, (size_t)st.st_size, PROT_READ, MAP_SHARED, fd, 0 ); if( addr != MAP_FAILED ) { bcf = malloc( (size_t)st.st_size ); if( !bcf ) { (void)munmap( addr, (size_t)st.st_size ); rlist_fatal( "Canot allocate memory for SHM/%s data", "csvn.bcf" ); return 0; } memcpy( bcf, addr, (size_t)st.st_size ); (void)munmap( addr, (size_t)st.st_size ); return (size_t)st.st_size; } else { rlist_fatal( "Canot mmap SHM/%s data", "csvn.bcf" ); return 0; } } else { rlist_fatal( "Canot stat SHM/%s data", "csvn.bcf" ); return 0; } } static void check_bcf_ident( const void *bf, size_t size ) { Bcf32_fhdr *fhdr = (Bcf32_fhdr *)bf; if( !fhdr ) rlist_fatal( "Invalid address of SHM/%s data", "csvn.bcf" ); if( (size_t)fhdr->b_fsize != size ) rlist_fatal( "Binary Config SHM/%s: invalid size", "csvn.bcf" ); if( memcmp( fhdr->b_ident, BCFMAG, (size_t)SZBCFMAG ) ) rlist_fatal( "Binary Config SHM/%s: invalid MAGIC number", "csvn.bcf" ); if( fhdr->b_ident[BI_CLASS] != BCF_CLASS_32 ) rlist_fatal( "Binary Config SHM/%s: invalid objects class", "csvn.bcf" ); #if __BYTE_ORDER == __LITTLE_ENDIAN if( fhdr->b_ident[BI_DATA] != BCF_DATA_LSB ) rlist_fatal( "Binary Config SHM/%s: invalid byte-order", "csvn.bcf" ); #else if( fhdr->b_ident[BI_DATA] != BCF_DATA_MSB ) rlist_fatal( "Binary Config SHM/%s: invalid byte-order", "csvn.bcf" ); #endif if( fhdr->b_ident[BI_VERSION] != BV_CURRENT ) rlist_fatal( "Binary Config SHM/%s: invalid version", "csvn.bcf" ); } /*************************************************************** Print config file functions: */ static void __print_global_variable( void *data, void *user_data ) { struct variable *variable = (struct variable *)data; struct strbuf *sb = (struct strbuf *)user_data; if( !variable || !sb ) return; switch( variable->type ) { case DT_NUMERICAL: strbuf_addf( sb, " %s = %d;\n", variable->name, variable->_v.val ); break; case DT_PATH: strbuf_addf( sb, " %s = '%s';\n", variable->name, variable->_v.vptr ); break; case DT_STRING: strbuf_addf( sb, " %s = \"%s\";\n", variable->name, variable->_v.vptr ); break; default: break; } } static void print_global_variables( struct strbuf *sb, struct dlist *list ) { if( list ) { dlist_foreach( list, __print_global_variable, (void *)sb ); } } static void __print_repo_variable( void *data, void *user_data ) { struct variable *variable = (struct variable *)data; struct strbuf *sb = (struct strbuf *)user_data; if( !variable || !sb ) return; switch( variable->type ) { case DT_NUMERICAL: strbuf_addf( sb, " %s = %d;\n", variable->name, variable->_v.val ); break; case DT_PATH: strbuf_addf( sb, " %s = '%s';\n", variable->name, variable->_v.vptr ); break; case DT_STRING: strbuf_addf( sb, " %s = \"%s\";\n", variable->name, variable->_v.vptr ); break; default: break; } } static void print_repo_variables( struct strbuf *sb, struct dlist *list ) { if( list ) { dlist_foreach( list, __print_repo_variable, (void *)sb ); } } static void __print_repo( void *data, void *user_data ) { struct repo *repo = (struct repo *)data; struct strbuf *sb = (struct strbuf *)user_data; if( !repo || !sb ) return; strbuf_addf( sb, " repo '%s' {\n", repo->path ); if( repo->list ) { print_repo_variables( sb, repo->list ); } strbuf_addf( sb, " }\n\n" ); } static void print_repos( struct strbuf *sb, struct dlist *list ) { if( list ) { dlist_foreach( list, __print_repo, (void *)sb ); } } static void __print_section( void *data, void *user_data ) { struct section *section = (struct section *)data; struct strbuf *sb = (struct strbuf *)user_data; if( !section || !sb ) return; if( section->list ) { if( section->type == ST_REPOS ) { strbuf_addf( sb, "section \"%s\" {\n\n", section->name ); print_repos( sb, section->list ); strbuf_addf( sb, "}\n\n" ); } else if( section->type == ST_GLOBAL ) { strbuf_addf( sb, "section \".global\" {\n" ); print_global_variables( sb, section->list ); strbuf_addf( sb, "}\n\n" ); } } } void print_config( struct strbuf *sb, struct dlist *list ) { strbuf_addf( sb, "
\n" );
  strbuf_addf( sb, "/*******************************************************\n" );
  strbuf_addf( sb, "  Global variables are propagate into repo sections but\n" );
  strbuf_addf( sb, "  their values overrides by correspond repo variables.\n" );
  strbuf_addf( sb, " */\n\n" );
  if( list ) { dlist_foreach( list, __print_section, (void *)sb ); }
  strbuf_addf( sb, "
\n" ); } /* End of print config file functions. ***************************************************************/ /************************** lookup variable in repo: */ static int __compare_variables_by_name( const void *a, const void *b ) { struct variable *va = (struct variable *)a; struct variable *vb = (struct variable *)b; if( !va || !vb ) return -1; return strcmp( (const char *)va->name, (const char *)vb->name ); } struct variable *lookup( struct repo *repo, struct variable *variable ) { struct dlist *found = NULL; if( !repo || !repo->list ) return NULL; found = dlist_find_data( repo->list, __compare_variables_by_name, (const void *)variable ); if( found ) return (struct variable *)(found->data); return NULL; } struct variable *lookup_global( struct section *section, struct variable *variable ) { struct dlist *found = NULL; if( !section || !section->list ) return NULL; found = dlist_find_data( section->list, __compare_variables_by_name, (const void *)variable ); if( found ) return (struct variable *)(found->data); return NULL; } /************** lookup repo: */ struct repo *lookup_repo( struct dlist *config, const char *path ) { struct dlist *list = NULL; if( !config || !path ) return NULL; list = config; while( list ) { struct section *section = (struct section *)list->data; if( section->type == ST_REPOS ) { struct dlist *rlist = section->list; while( rlist ) { struct repo *repo = (struct repo *)rlist->data; if( !strcmp( (const char *)repo->path, path ) ) return repo; rlist = dlist_next( rlist ); } } list = dlist_next( list ); } return NULL; } /***************** lookup section: */ struct section *lookup_section( struct dlist *config, const char *name ) { struct dlist *list = NULL; if( !config || !name ) return NULL; list = config; while( list ) { struct section *section = (struct section *)list->data; if( section && section->type == ST_REPOS && !strcmp( (const char *)section->name, name ) ) return section; list = dlist_next( list ); } return NULL; } /************************ lookup global section: */ struct section *lookup_global_section( struct dlist *config ) { struct dlist *list = NULL; if( !config ) return NULL; list = config; while( list ) { struct section *section = (struct section *)list->data; if( section && section->type == ST_GLOBAL ) return section; list = dlist_next( list ); } return NULL; } /****************** repolist length: */ int repolist_length( struct dlist *config ) { struct dlist *list = NULL; int length = 0; if( !config ) return length; list = config; while( list ) { struct section *section = (struct section *)list->data; if( section->type == ST_REPOS ) { struct dlist *rlist = section->list; while( rlist ) { ++length; rlist = dlist_next( rlist ); } } list = dlist_next( list ); } return length; } /*************** repolist nth: */ struct repo *repolist_nth( struct dlist *config, int n ) { struct dlist *list = NULL; int length = 0; if( !config || n < 0 ) return NULL; list = config; while( list ) { struct section *section = (struct section *)list->data; if( section->type == ST_REPOS ) { struct dlist *rlist = section->list; while( rlist ) { if( length == n ) { struct repo *repo = (struct repo *)rlist->data; return repo; } ++length; rlist = dlist_next( rlist ); } } list = dlist_next( list ); } return NULL; } /************************** repo position in config: */ int repo_position( struct dlist *config, struct repo *repo ) { struct dlist *list = NULL; int position = -1; if( !config || !repo ) return position; position = 0; list = config; while( list ) { struct section *section = (struct section *)list->data; if( section->type == ST_REPOS ) { struct dlist *rlist = section->list; while( rlist ) { if( (struct repo *)rlist->data == repo ) { return position; } ++position; rlist = dlist_next( rlist ); } } list = dlist_next( list ); } return -1; } /************************************** parent section node of repolist nth: */ struct dlist *parent_section_node_repolist_nth( struct dlist *config, int n ) { struct dlist *list = NULL; int length = 0; if( !config || n < 0 ) return NULL; list = config; while( list ) { struct section *section = (struct section *)list->data; if( section->type == ST_REPOS ) { struct dlist *rlist = section->list; while( rlist ) { if( length == n ) { return list; } ++length; rlist = dlist_next( rlist ); } } list = dlist_next( list ); } return NULL; } /************************************ parent rlist node of repolist nth: */ struct dlist *parent_rlist_node_repolist_nth( struct dlist *config, int n ) { struct dlist *list = NULL; int length = 0; if( !config || n < 0 ) return NULL; list = config; while( list ) { struct section *section = (struct section *)list->data; if( section->type == ST_REPOS ) { struct dlist *rlist = section->list; while( rlist ) { if( length == n ) { return rlist; } ++length; rlist = dlist_next( rlist ); } } list = dlist_next( list ); } return NULL; } /********************************************** Provide all global variables into each repo if that variable is not present in the repo: */ static void __provide_foreach_data( void *data, void *user_data ) { struct variable *variable = (struct variable *)data; struct repo *repo = (struct repo *)user_data; if( !repo || !variable ) return; if( !lookup( repo, variable ) ) { struct variable *var = (struct variable *)xmalloc( sizeof(struct variable) ); memcpy( (void *)var, (void *)variable, sizeof(struct variable) ); repo->list = dlist_append( repo->list, (void *)var ); } } static void __provide_foreach_repos( void *data, void *user_data ) { struct section *global = (struct section *)user_data; if( !global || !global->list ) return; dlist_foreach( global->list, __provide_foreach_data, data ); } static void __provide_foreach_sections( void *data, void *user_data ) { struct section *section = (struct section *)data; if( !section || section->type == ST_GLOBAL || !section->list ) return; dlist_foreach( section->list, __provide_foreach_repos, user_data ); } static void provide_global_data( struct dlist *tree, struct section *global ) { if( tree ) { dlist_foreach( tree, __provide_foreach_sections, (void *)global ); } } /* **********************************************/ /********************************************** Free REPO list functions: */ static void __free_variable( void *data, void *user_data ) { struct variable *variable = (struct variable *)data; if( variable ) free( variable ); } static void free_variables( struct dlist *list ) { if( list ) { dlist_free( list, __free_variable ); } } static void __free_repo( void *data, void *user_data ) { struct repo *repo = (struct repo *)data; if( !repo ) return; if( repo->list ) { free_variables( repo->list ); } free( repo ); } static void free_repos( struct dlist *list ) { if( list ) { dlist_free( list, __free_repo ); } } static void __free_section( void *data, void *user_data ) { struct section *section = (struct section *)data; if( !section ) return; if( section->list ) { if( section->type == ST_GLOBAL ) free_variables( section->list ); else if( section->type == ST_REPOS ) free_repos( section->list ); } free( section ); } static void free_repolist( struct dlist *list ) { if( list ) { dlist_free( list, __free_section ); } } /* **********************************************/ void free_config( void ) { free_repolist( config ); config = NULL; if( bcf ) { free( bcf ); bcf = NULL; } } static struct dlist *read_repolist( const void *bf ) { struct dlist *tree = NULL; Bcf32_fhdr *fhdr = NULL; unsigned char *ftab, *stab; ftab = (unsigned char *)bf; fhdr = (Bcf32_fhdr *)ftab; if( !fhdr ) return tree; stab = (unsigned char *)(ftab + (int)fhdr->b_stoff); #if __DEBUG__ == 1 fprintf( stderr, "BCF: header's size in bytes: %d\n", fhdr->b_hsize ); fprintf( stderr, "BCF: Whole BCF file size in bytes: %d\n", fhdr->b_fsize ); fprintf( stderr, "BCF: section header table’s file offset in bytes: %d\n", fhdr->b_shoff ); fprintf( stderr, "BCF: section header's size in bytes: %d\n", fhdr->b_shentsize ); fprintf( stderr, "BCF: number of entries in section headers table: %d\n", fhdr->b_shnum ); fprintf( stderr, "BCF: repository header table’s file offset in bytes: %d\n", fhdr->b_rhoff ); fprintf( stderr, "BCF: repository header's size in bytes: %d\n", fhdr->b_rhentsize ); fprintf( stderr, "BCF: number of entries in repository headers table: %d\n", fhdr->b_rhnum ); fprintf( stderr, "BCF: data entries table’s file offset in bytes: %d\n", fhdr->b_dtoff ); fprintf( stderr, "BCF: data entry's size in bytes: %d\n", fhdr->b_dtentsize ); fprintf( stderr, "BCF: number of entries in data entries table: %d\n", fhdr->b_dtnum ); fprintf( stderr, "BCF: string table’s file offset in bytes: %d\n\n", fhdr->b_stoff ); #endif { Bcf32_shdr *shdr = NULL; int s = 0; struct section *global = NULL; shdr = (Bcf32_shdr *)((unsigned char *)bf + (int)fhdr->b_shoff); while( s < fhdr->b_shnum ) { #if __DEBUG__ == 1 fprintf( stderr, "SECTION: s_name: %s\n", shdr->s_name ); fprintf( stderr, "SECTION: s_type: %d\n", shdr->s_type ); fprintf( stderr, "SECTION: s_shdr (offset in string table): %d\n", shdr->s_shdr ); fprintf( stderr, "SECTION: s_sdata (file offset to data): %d\n", shdr->s_sdata ); fprintf( stderr, "SECTION: s_dnum (number of data entries): %d\n", shdr->s_dnum ); fprintf( stderr, "SECTION: name: \"%s\"\n\n", stab + (int)shdr->s_shdr ); #endif if( shdr->s_type == ST_GLOBAL ) { Bcf32_dntr *dntr = NULL; int d = 0; struct section *sec = NULL; sec = (struct section *)xmalloc( sizeof(struct section) ); sec->type = ST_GLOBAL; sec->name = (unsigned char *)(stab + (int)shdr->s_shdr); /*do not allocate, all in bcf */ tree = dlist_append( tree, (void *)sec ); global = sec; dntr = (Bcf32_dntr *)(ftab + (int)shdr->s_sdata); while( d < shdr->s_dnum ) { #if __DEBUG__ == 1 fprintf( stderr, "DATA: d_name: %s\n", stab + (int)dntr->d_name ); #endif switch( dntr->d_type ) { case DT_NUMERICAL: #if __DEBUG__ == 1 fprintf( stderr, "DATA: _v.d_value: %d\n\n", dntr->_v.d_value ); #endif { struct variable *var = NULL; var = (struct variable *)xmalloc( sizeof(struct variable) ); var->type = DT_NUMERICAL; var->name = (unsigned char *)(stab + (int)dntr->d_name); /*do not allocate, all in bcf */ var->_v.val = (int)dntr->_v.d_value; sec->list = dlist_append( sec->list, (void *)var ); } break; case DT_PATH: #if __DEBUG__ == 1 fprintf( stderr, "DATA: _v.d_valptr (path): '%s';\n\n", stab + (int)dntr->_v.d_valptr ); #endif { struct variable *var = NULL; var = (struct variable *)xmalloc( sizeof(struct variable) ); var->type = DT_PATH; var->name = (unsigned char *)(stab + (int)dntr->d_name); /*do not allocate, all in bcf */ var->_v.vptr = (unsigned char *)(stab + (int)dntr->_v.d_valptr); sec->list = dlist_append( sec->list, (void *)var ); } break; case DT_STRING: #if __DEBUG__ == 1 fprintf( stderr, "DATA: _v.d_valptr (string): \"%s\";\n\n", stab + (int)dntr->_v.d_valptr ); #endif { struct variable *var = NULL; var = (struct variable *)xmalloc( sizeof(struct variable) ); var->type = DT_STRING; var->name = (unsigned char *)(stab + (int)dntr->d_name); /*do not allocate, all in bcf */ var->_v.vptr = (unsigned char *)(stab + (int)dntr->_v.d_valptr); sec->list = dlist_append( sec->list, (void *)var ); } break; default: break; } ++d; ++dntr; } /* End of while( global data ) */ } else if( shdr->s_type == ST_REPOS ) { Bcf32_rhdr *rhdr = NULL; int r = 0; struct section *sec = NULL; sec = (struct section *)xmalloc( sizeof(struct section) ); sec->type = ST_REPOS; sec->name = (unsigned char *)(stab + (int)shdr->s_shdr); /*do not allocate, all in bcf */ tree = dlist_append( tree, (void *)sec ); rhdr = (Bcf32_rhdr *)(ftab + (int)shdr->s_sdata); while( r < shdr->s_dnum ) { int d = 0; Bcf32_dntr *dntr = NULL; struct repo *repo = NULL; repo = (struct repo *)xmalloc( sizeof(struct repo) ); repo->path = (unsigned char *)(stab + (int)rhdr->r_rhdr); /*do not allocate, all in bcf */ sec->list = dlist_append( sec->list, (void *)repo ); #if __DEBUG__ == 1 fprintf( stderr, "REPO: path: %s\n", stab + (int)rhdr->r_rhdr ); #endif dntr = (Bcf32_dntr *)(ftab + (int)rhdr->r_rdata); while( d < rhdr->r_dnum ) { #if __DEBUG__ == 1 fprintf( stderr, "REPO's DATA: d_name: %s\n", stab + (int)dntr->d_name ); #endif switch( dntr->d_type ) { case DT_NUMERICAL: #if __DEBUG__ == 1 fprintf( stderr, "REPO's DATA: _v.d_value: %d\n\n", dntr->_v.d_value ); #endif { struct variable *var = NULL; var = (struct variable *)xmalloc( sizeof(struct variable) ); var->type = DT_NUMERICAL; var->name = (unsigned char *)(stab + (int)dntr->d_name); /*do not allocate, all in bcf */ var->_v.val = (int)dntr->_v.d_value; repo->list = dlist_append( repo->list, (void *)var ); } break; case DT_PATH: #if __DEBUG__ == 1 fprintf( stderr, "REPO's DATA: _v.d_valptr (path): '%s';\n\n", stab + (int)dntr->_v.d_valptr ); #endif { struct variable *var = NULL; var = (struct variable *)xmalloc( sizeof(struct variable) ); var->type = DT_PATH; var->name = (unsigned char *)(stab + (int)dntr->d_name); /*do not allocate, all in bcf */ var->_v.vptr = (unsigned char *)(stab + (int)dntr->_v.d_valptr); repo->list = dlist_append( repo->list, (void *)var ); } break; case DT_STRING: #if __DEBUG__ == 1 fprintf( stderr, "REPO's DATA: _v.d_valptr (string): \"%s\";\n\n", stab + (int)dntr->_v.d_valptr ); #endif { struct variable *var = NULL; var = (struct variable *)xmalloc( sizeof(struct variable) ); var->type = DT_STRING; var->name = (unsigned char *)(stab + (int)dntr->d_name); /*do not allocate, all in bcf */ var->_v.vptr = (unsigned char *)(stab + (int)dntr->_v.d_valptr); repo->list = dlist_append( repo->list, (void *)var ); } break; default: break; } ++d; ++dntr; } /* End of while( repo data ) */ ++r; ++rhdr; } /* End of while( repos ) */ #if __DEBUG__ == 1 fprintf( stderr, "\n" ); #endif } else { rlist_fatal( "Invalid section in the SHM/%s data", "csvn.bcf" ); #if __DEBUG__ == 1 fprintf( stderr, "SECTION: empty\n" ); #endif } ++s; ++shdr; } /* End of while( sections ) */ if( global ) provide_global_data( tree, global ); } return tree; } struct dlist *read_config( void ) { size_t bsize = 0; if( (bsize = read_bcf()) ) { check_bcf_ident( bcf, bsize ); /* fatal on error */ return read_repolist( bcf ); } return NULL; }