/*- * Copyright (c) 2001 * Tatsuya Kudoh(CDR/TK),ROYALPANDA. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* ** Falling Tower KAI ** Copyright (c) 2000 ROYALPANDA All rights reserved ** ** May 25, 2000 Ver 1.0 */ #include #include #include #include #include #include #include"record.h" #define FS '\t' /* field separator */ /**************************************** ** ** Field parsers ** *****************************************/ static int read_num( char **p ) { char *q; int i; i = 0; q = *p; if( !isdigit(*q) ) return -1; while( *q && *q != FS && *q != '\n' ){ if( !isdigit(*q) ) return -1; i = i * 10 + *q - '0'; q++; } if( *q == FS ) *p = q + 1; else *p = q; return i; } static int read_str( char **p, char *buf, int bufsiz ) { char *q; int len; if( --bufsiz < 1 ) return -1; len = 0; q = *p; while( *q && *q != FS && *q != '\n' ){ if( iscntrl(*q) ) *buf++ = ' '; else *buf++ = *q; q++; if( ++len == bufsiz ) break; } *buf = '\0'; if( *q == FS ) *p = q + 1; else *p = q; return len; } /************************************* * * record database transactions * *************************************/ /* unlock */ static int unlock( record_file_t *rp ) { if( rp->lock_fd < 0 ) return 0; unlink(rp->lock_file); close(rp->lock_fd); rp->lock_fd = -1; return 0; } /* ** lock for reading */ int lock_read( record_file_t *rp ) { int i; if( rp->lock_fd < 0 ){ i = open(rp->lock_file,O_CREAT|O_WRONLY|O_SHLOCK,0777); if( i < 0 ){ message("Cannot open lock file."); return -1; } rp->lock_fd = i; } return 0; } /* ** lock for writing */ int lock_write( record_file_t *rp ) { int i; if( rp->lock_fd < 0 ){ i = open(rp->lock_file,O_CREAT|O_WRONLY|O_EXLOCK,0600); if( i < 0 ){ message("Cannot open lock file."); return -1; } rp->lock_fd = i; }else{ flock(rp->lock_fd,LOCK_EX); } return 0; } /* ** commit */ static int commit( record_file_t *rp ) { return rename(rp->swap_file, rp->record_file); } /* ** restore */ static int restore( record_file_t *rp ) { int fd; if(lock_write(rp) < 0 ) return -1; if( (fd = open(rp->record_file,O_RDONLY)) != -1 ){ close(fd); return 1; } if( errno != ENOENT ){ message("Cannnot open record file."); return -1; } if( rename(rp->swap_file, rp->record_file) < 0 ){ if( errno != ENOENT ){ message("Cannot write record file."); return -1; }else{ return 0; } } return 1; } /* ** read records from file ** try to restore from swap when record file is not found. */ static int read_record( record_file_t *rp ) { FILE *fp; char buf[80]; char *str; int i,l; int score; record_t *p; char *q; if( (fp = fopen(rp->record_file,"r")) == NULL ){ if( errno == ENOENT ){ i = restore(rp); if( i < 0 ) return -1; if( i ){ if( (fp = fopen(rp->record_file,"r")) == NULL ){ message("Cannot open record file."); return -1; } }else{ rp->num_record = 0; return 0; } } }else{ /* ** If swap file exists and its i-node is same as record file, ** writing to swap file damages record file. ** So, remove swap file. */ unlink(rp->swap_file); } i = 0; score = 99999; /* most high value */ p = rp->record; while( i < rp->max_record && fgets(buf,80,fp) != NULL ){ str = buf; if( (p->score = read_num(&str)) < 0 ) continue; if( p->score > score ) continue; if( (p->uid = read_num(&str)) < 0 ) continue; if( (p->date = read_num(&str)) < 0 ) continue; if( read_str(&str,p->name,32) < 0 ) continue; score = p->score; p++; i++; } if( ferror(fp) ){ message("File reading error."); i = -1; } fclose(fp); rp->num_record = i; return i; } static int write_record( record_file_t *rp ) { FILE *fp; int i,new_data; record_t *p,*np; char buf[80]; np = rp->new_record; new_data = 1; if( rp->override ){ for( p = rp->record, i = 0 ; i < rp->num_record ; i++, p++ ){ if( p->uid == np->uid ){ if( p->score >= np->score ){ return 0; }else{ new_data = 0; break; } } } } if( new_data ){ if( rp->num_record < rp->max_record ) rp->num_record++; else if( rp->record[rp->num_record-1].score >= np->score ) return 0; p = &rp->record[rp->num_record-1]; } p--; while( p >= rp->record && p->score < np->score ){ p[1] = p[0]; p--; } p++; *p = *np; if( (fp = fopen(rp->swap_file,"w")) == NULL ){ message("Cannot open swap file."); return -1; } for( p = rp->record, i = 0 ; i < rp->num_record ; p++, i++ ){ sprintf(buf,"%d%c%d%c%d%c%-0.31s\n", p->score,FS, p->uid,FS, p->date,FS, p->name); if( fputs(buf,fp) == EOF ){ fclose(fp); unlink(rp->swap_file); message("File writing error."); return -1; } } if( fclose(fp) == EOF ){ unlink(rp->swap_file); message("Cannot close swap file."); return -1; } if( commit(rp) < 0 ) return -1; return 1; } /******* * * APIs * *******/ int RECORD_read( record_file_t *rp ) { int i; if( lock_read(rp) < 0 ) return -1; i = read_record(rp); unlock(rp); return i; } int RECORD_write( record_file_t *rp ) { int i; if( lock_write(rp) < 0 ) return -1; if( read_record(rp) < 0 ){ unlock(rp); return -1; } i = write_record(rp); unlock(rp); return i; } int RECORD_init( record_file_t *rp ) { record_t *p; p = (record_t*)malloc(sizeof(record_t)*rp->max_record); if( p == NULL ){ message("Memory allocation error."); return -1; } rp->record = p; rp->lock_fd = -1; rp->num_record = 0; #if SETUID /* ** xjumpx mode is 4755, owner is games. ** only games user (including xjumpx) write score file. */ umask(022); #else /* ** everybody can access score file. ** so, score directory mode must be 770, ** xjump mode must be 2755, group is games */ umask(000); #endif return RECORD_read(rp); }