/* +----------------------------------------------------------------------+ | PHP version 4.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Jim Winstead (jimw@php.net) | +----------------------------------------------------------------------+ */ #if defined(COMPILE_DL) || defined(COMPILE_DL_DBASE) #include "dl/phpdl.h" #endif #include #ifdef HAVE_SYS_TYPES_H #include #endif #include "php.h" #include "safe_mode.h" #include "fopen-wrappers.h" #include "php_globals.h" #if DBASE #include "php_dbase.h" #include "dbf.h" #if defined(THREAD_SAFE) DWORD DbaseTls; static int numthreads=0; void *dbase_mutex; typedef struct dbase_global_struct{ int le_dbhead; }dbase_global_struct; #define DBase_GLOBAL(a) dbase_globals->a #define DBase_TLS_VARS \ dbase_global_struct *dbase_globals; \ dbase_globals=TlsGetValue(DbaseTls); #else static int le_dbhead; #define DBase_GLOBAL(a) a #define DBase_TLS_VARS #endif #include #include static void _close_dbase(dbhead_t *dbhead) { close(dbhead->db_fd); free_dbf_head(dbhead); } PHP_MINIT_FUNCTION(dbase) { #if defined(THREAD_SAFE) dbase_global_struct *dbase_globals; #if defined(COMPILE_DL) || defined(COMPILE_DL_DBASE) CREATE_MUTEX(dbase_mutex,"DBase_TLS"); SET_MUTEX(dbase_mutex); numthreads++; if (numthreads==1){ if ((DbaseTls=TlsAlloc())==0xFFFFFFFF){ FREE_MUTEX(dbase_mutex); return 0; }} FREE_MUTEX(dbase_mutex); #endif dbase_globals = (dbase_global_struct *) LocalAlloc(LPTR, sizeof(dbase_global_struct)); TlsSetValue(DbaseTls, (void *) dbase_globals); #endif DBase_GLOBAL(le_dbhead) = register_list_destructors(_close_dbase,NULL); return SUCCESS; } static PHP_MSHUTDOWN_FUNCTION(dbase) { #if defined(THREAD_SAFE) dbase_global_struct *dbase_globals; dbase_globals = TlsGetValue(DbaseTls); if (dbase_globals != 0) LocalFree((HLOCAL) dbase_globals); #if defined(COMPILE_DL) || defined(COMPILE_DL_DBASE) SET_MUTEX(dbase_mutex); numthreads--; if (!numthreads){ if (!TlsFree(DbaseTls)){ FREE_MUTEX(dbase_mutex); return 0; }} FREE_MUTEX(dbase_mutex); #endif #endif return SUCCESS; } /* {{{ proto int dbase_open(string name, int mode) Opens a dBase-format database file */ PHP_FUNCTION(dbase_open) { pval *dbf_name, *options; dbhead_t *dbh; int handle; PLS_FETCH(); DBase_TLS_VARS; if (ARG_COUNT(ht) != 2 || getParameters(ht,2,&dbf_name,&options)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(dbf_name); convert_to_long(options); if (PG(safe_mode) && (!php_checkuid(dbf_name->value.str.val, 2))) { RETURN_FALSE; } if (php_check_open_basedir(dbf_name->value.str.val)) { RETURN_FALSE; } dbh = dbf_open(dbf_name->value.str.val, options->value.lval); if (dbh == NULL) { php_error(E_WARNING, "unable to open database %s", dbf_name->value.str.val); RETURN_FALSE; } handle = zend_list_insert(dbh, DBase_GLOBAL(le_dbhead)); RETURN_LONG(handle); } /* }}} */ /* {{{ proto bool dbase_close(int identifier) Closes an open dBase-format database file */ PHP_FUNCTION(dbase_close) { pval *dbh_id; dbhead_t *dbh; int dbh_type; DBase_TLS_VARS; if (ARG_COUNT(ht) != 1 || getParameters(ht,1,&dbh_id)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } zend_list_delete(dbh_id->value.lval); RETURN_TRUE; } /* }}} */ /* {{{ proto int dbase_numrecords(int identifier) Returns the number of records in the database */ PHP_FUNCTION(dbase_numrecords) { pval *dbh_id; dbhead_t *dbh; int dbh_type; DBase_TLS_VARS; if (ARG_COUNT(ht) != 1 || getParameters(ht,1,&dbh_id)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } RETURN_LONG(dbh->db_records); } /* }}} */ /* {{{ proto int dbase_numfields(int identifier) Returns the number of fields (columns) in the database */ PHP_FUNCTION(dbase_numfields) { pval *dbh_id; dbhead_t *dbh; int dbh_type; DBase_TLS_VARS; if (ARG_COUNT(ht) != 1 || getParameters(ht,1,&dbh_id)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } RETURN_LONG(dbh->db_nfields); } /* }}} */ /* {{{ proto bool dbase_pack(int identifier) Packs the database (deletes records marked for deletion) */ PHP_FUNCTION(dbase_pack) { pval *dbh_id; dbhead_t *dbh; int dbh_type; DBase_TLS_VARS; if (ARG_COUNT(ht) != 1 || getParameters(ht,1,&dbh_id)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } pack_dbf(dbh); put_dbf_info(dbh); RETURN_TRUE; } /* }}} */ /* {{{ proto bool dbase_add_record(int identifier, array data) Adds a record to the database */ PHP_FUNCTION(dbase_add_record) { pval *dbh_id, *fields, *field; dbhead_t *dbh; int dbh_type; int num_fields; dbfield_t *dbf, *cur_f; char *cp, *t_cp; int i; DBase_TLS_VARS; if (ARG_COUNT(ht) != 2 || getParameters(ht,2,&dbh_id,&fields)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); if (fields->type != IS_ARRAY) { php_error(E_WARNING, "Expected array as second parameter"); RETURN_FALSE; } dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } num_fields = zend_hash_num_elements(fields->value.ht); if (num_fields != dbh->db_nfields) { php_error(E_WARNING, "Wrong number of fields specified"); RETURN_FALSE; } cp = t_cp = (char *)emalloc(dbh->db_rlen + 1); if (!cp) { php_error(E_WARNING, "unable to allocate memory"); RETURN_FALSE; } *t_cp++ = VALID_RECORD; dbf = dbh->db_fields; for (i = 0, cur_f = dbf; cur_f < &dbf[num_fields]; i++, cur_f++) { if (zend_hash_index_find(fields->value.ht, i, (void **)&field) == FAILURE) { php_error(E_WARNING, "unexpected error"); efree(cp); RETURN_FALSE; } convert_to_string(field); sprintf(t_cp, cur_f->db_format, field->value.str.val); t_cp += cur_f->db_flen; } dbh->db_records++; if (put_dbf_record(dbh, dbh->db_records, cp) < 0) { php_error(E_WARNING, "unable to put record at %ld", dbh->db_records); efree(cp); RETURN_FALSE; } put_dbf_info(dbh); efree(cp); RETURN_TRUE; } /* }}} */ /* {{{ proto bool dbase_replace_record(int identifier, array data, int recnum) Replaces a record to the database */ PHP_FUNCTION(dbase_replace_record) { pval *dbh_id, *fields, *field, *recnum; dbhead_t *dbh; int dbh_type; int num_fields; dbfield_t *dbf, *cur_f; char *cp, *t_cp; int i; DBase_TLS_VARS; if (ARG_COUNT(ht) != 3 || getParameters(ht,3,&dbh_id,&fields,&recnum)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); convert_to_long(recnum); if (fields->type != IS_ARRAY) { php_error(E_WARNING, "Expected array as second parameter"); RETURN_FALSE; } dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } num_fields = zend_hash_num_elements(fields->value.ht); if (num_fields != dbh->db_nfields) { php_error(E_WARNING, "Wrong number of fields specified"); RETURN_FALSE; } cp = t_cp = (char *)emalloc(dbh->db_rlen + 1); if (!cp) { php_error(E_WARNING, "unable to allocate memory"); RETURN_FALSE; } *t_cp++ = VALID_RECORD; dbf = dbh->db_fields; for (i = 0, cur_f = dbf; cur_f < &dbf[num_fields]; i++, cur_f++) { if (zend_hash_index_find(fields->value.ht, i, (void **)&field) == FAILURE) { php_error(E_WARNING, "unexpected error"); efree(cp); RETURN_FALSE; } convert_to_string(field); sprintf(t_cp, cur_f->db_format, field->value.str.val); t_cp += cur_f->db_flen; } if (put_dbf_record(dbh, recnum->value.lval, cp) < 0) { php_error(E_WARNING, "unable to put record at %ld", dbh->db_records); efree(cp); RETURN_FALSE; } put_dbf_info(dbh); efree(cp); RETURN_TRUE; } /* }}} */ /* {{{ proto bool dbase_delete_record(int identifier, int record) Marks a record to be deleted */ PHP_FUNCTION(dbase_delete_record) { pval *dbh_id, *record; dbhead_t *dbh; int dbh_type; DBase_TLS_VARS; if (ARG_COUNT(ht) != 2 || getParameters(ht,2,&dbh_id,&record)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); convert_to_long(record); dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } if (del_dbf_record(dbh, record->value.lval) < 0) { if (record->value.lval > dbh->db_records) { php_error(E_WARNING, "record %d out of bounds", record->value.lval); } else { php_error(E_WARNING, "unable to delete record %d", record->value.lval); } RETURN_FALSE; } put_dbf_info(dbh); RETURN_TRUE; } /* }}} */ /* {{{ proto array dbase_get_record(int identifier, int record) Returns an array representing a record from the database */ PHP_FUNCTION(dbase_get_record) { pval *dbh_id, *record; dbhead_t *dbh; int dbh_type; dbfield_t *dbf, *cur_f; char *data, *fnp, *str_value; size_t cursize = 0; DBase_TLS_VARS; if (ARG_COUNT(ht) != 2 || getParameters(ht,2,&dbh_id,&record)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); convert_to_long(record); dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } if ((data = get_dbf_record(dbh, record->value.lval)) == NULL) { php_error(E_WARNING, "Tried to read bad record %d", record->value.lval); RETURN_FALSE; } dbf = dbh->db_fields; if (array_init(return_value) == FAILURE) { RETURN_FALSE; } fnp = NULL; for (cur_f = dbf; cur_f < &dbf[dbh->db_nfields]; cur_f++) { /* get the value */ str_value = (char *)emalloc(cur_f->db_flen + 1); if(cursize <= cur_f->db_flen) { cursize = cur_f->db_flen + 1; fnp = erealloc(fnp, cursize); } snprintf(str_value, cursize, cur_f->db_format, get_field_val(data, cur_f, fnp)); /* now convert it to the right php internal type */ switch (cur_f->db_type) { case 'C': case 'D': add_next_index_string(return_value,str_value,1); break; case 'N': /* FALLS THROUGH */ case 'L': /* FALLS THROUGH */ if (cur_f->db_fdc == 0) { add_next_index_long(return_value, strtol(str_value, NULL, 10)); } else { add_next_index_double(return_value, atof(str_value)); } break; case 'M': /* this is a memo field. don't know how to deal with this yet */ break; default: /* should deal with this in some way */ break; } efree(str_value); } efree(fnp); /* mark whether this record was deleted */ if (data[0] == '*') { add_assoc_long(return_value,"deleted",1); } else { add_assoc_long(return_value,"deleted",0); } free(data); } /* }}} */ /* From Martin Kuba */ /* {{{ proto array dbase_get_record_with_names(int identifier, int record) Returns an associative array representing a record from the database */ PHP_FUNCTION(dbase_get_record_with_names) { pval *dbh_id, *record; dbhead_t *dbh; int dbh_type; dbfield_t *dbf, *cur_f; char *data, *fnp, *str_value; DBase_TLS_VARS; if (ARG_COUNT(ht) != 2 || getParameters(ht,2,&dbh_id,&record)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_long(dbh_id); convert_to_long(record); dbh = zend_list_find(dbh_id->value.lval, &dbh_type); if (!dbh || dbh_type != DBase_GLOBAL(le_dbhead)) { php_error(E_WARNING, "Unable to find database for identifier %d", dbh_id->value.lval); RETURN_FALSE; } if ((data = get_dbf_record(dbh, record->value.lval)) == NULL) { php_error(E_WARNING, "Tried to read bad record %d", record->value.lval); RETURN_FALSE; } dbf = dbh->db_fields; if (array_init(return_value) == FAILURE) { RETURN_FALSE; } fnp = (char *)emalloc(dbh->db_rlen); for (cur_f = dbf; cur_f < &dbf[dbh->db_nfields]; cur_f++) { /* get the value */ str_value = (char *)emalloc(cur_f->db_flen + 1); sprintf(str_value, cur_f->db_format, get_field_val(data, cur_f, fnp)); /* now convert it to the right php internal type */ switch (cur_f->db_type) { case 'C': case 'D': add_assoc_string(return_value,cur_f->db_fname,str_value,1); break; case 'N': /* FALLS THROUGH */ case 'L': /* FALLS THROUGH */ if (cur_f->db_fdc == 0) { add_assoc_long(return_value,cur_f->db_fname,strtol(str_value, NULL, 10)); } else { add_assoc_double(return_value,cur_f->db_fname,atof(str_value)); } break; case 'M': /* this is a memo field. don't know how to deal with this yet */ break; default: /* should deal with this in some way */ break; } efree(str_value); } efree(fnp); /* mark whether this record was deleted */ if (data[0] == '*') { add_assoc_long(return_value,"deleted",1); } else { add_assoc_long(return_value,"deleted",0); } free(data); } /* }}} */ /* {{{ proto bool dbase_create(string filename, array fields) Creates a new dBase-format database file */ PHP_FUNCTION(dbase_create) { pval *filename, *fields, *field, *value; int fd; dbhead_t *dbh; int num_fields; dbfield_t *dbf, *cur_f; int i, rlen, handle; PLS_FETCH(); DBase_TLS_VARS; if (ARG_COUNT(ht) != 2 || getParameters(ht,2,&filename,&fields)==FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(filename); if (fields->type != IS_ARRAY) { php_error(E_WARNING, "Expected array as second parameter"); RETURN_FALSE; } if (PG(safe_mode) && (!php_checkuid(Z_STRVAL_P(filename), 2))) { RETURN_FALSE; } if (php_check_open_basedir(Z_STRVAL_P(filename))) { RETURN_FALSE; } if ((fd = V_OPEN((Z_STRVAL_P(filename), O_BINARY|O_RDWR|O_CREAT, 0644))) < 0) { php_error(E_WARNING, "Unable to create database (%d): %s", errno, strerror(errno)); RETURN_FALSE; } num_fields = zend_hash_num_elements(fields->value.ht); /* have to use regular malloc() because this gets free()d by code in the dbase library */ dbh = (dbhead_t *)malloc(sizeof(dbhead_t)); dbf = (dbfield_t *)malloc(sizeof(dbfield_t) * num_fields); if (!dbh || !dbf) { php_error(E_WARNING, "Unable to allocate memory for header info"); RETURN_FALSE; } /* initialize the header structure */ dbh->db_fields = dbf; dbh->db_fd = fd; dbh->db_dbt = DBH_TYPE_NORMAL; strcpy(dbh->db_date, "19930818"); dbh->db_records = 0; dbh->db_nfields = num_fields; dbh->db_hlen = sizeof(struct dbf_dhead) + 2 + num_fields * sizeof(struct dbf_dfield); rlen = 1; for (i = 0, cur_f = dbf; i < num_fields; i++, cur_f++) { /* look up the first field */ if (zend_hash_index_find(fields->value.ht, i, (void **)&field) == FAILURE) { php_error(E_WARNING, "unable to find field %d", i); free_dbf_head(dbh); RETURN_FALSE; } if (field->type != IS_ARRAY) { php_error(E_WARNING, "second parameter must be array of arrays"); free_dbf_head(dbh); RETURN_FALSE; } /* field name */ if (zend_hash_index_find(field->value.ht, 0, (void **)&value) == FAILURE) { php_error(E_WARNING, "expected field name as first element of list in field %d", i); free_dbf_head(dbh); RETURN_FALSE; } convert_to_string(value); if (value->value.str.len > 10 || value->value.str.len == 0) { php_error(E_WARNING, "invalid field name '%s' (must be non-empty and less than or equal to 10 characters)", value->value.str.val); free_dbf_head(dbh); RETURN_FALSE; } copy_crimp(cur_f->db_fname, value->value.str.val, value->value.str.len); /* field type */ if (zend_hash_index_find(field->value.ht,1,(void **)&value) == FAILURE) { php_error(E_WARNING, "expected field type as sececond element of list in field %d", i); RETURN_FALSE; } convert_to_string(value); cur_f->db_type = toupper(*value->value.str.val); cur_f->db_fdc = 0; /* verify the field length */ switch (cur_f->db_type) { case 'L': cur_f->db_flen = 1; break; case 'M': cur_f->db_flen = 9; dbh->db_dbt = DBH_TYPE_MEMO; /* should create the memo file here, probably */ break; case 'D': cur_f->db_flen = 8; break; case 'N': case 'C': /* field length */ if (zend_hash_index_find(field->value.ht,2,(void **)&value) == FAILURE) { php_error(E_WARNING, "expected field length as third element of list in field %d", i); free_dbf_head(dbh); RETURN_FALSE; } convert_to_long(value); cur_f->db_flen = value->value.lval; if (cur_f->db_type == 'N') { if (zend_hash_index_find(field->value.ht,3,(void **)&value) == FAILURE) { php_error(E_WARNING, "expected field precision as fourth element of list in field %d", i); free_dbf_head(dbh); RETURN_FALSE; } convert_to_long(value); cur_f->db_fdc = value->value.lval; } break; default: php_error(E_WARNING, "unknown field type '%c'", cur_f->db_type); } cur_f->db_foffset = rlen; rlen += cur_f->db_flen; cur_f->db_format = get_dbf_f_fmt(cur_f); } dbh->db_rlen = rlen; put_dbf_info(dbh); handle = zend_list_insert(dbh, DBase_GLOBAL(le_dbhead)); RETURN_LONG(handle); } /* }}} */ function_entry dbase_functions[] = { PHP_FE(dbase_open, NULL) PHP_FE(dbase_create, NULL) PHP_FE(dbase_close, NULL) PHP_FE(dbase_numrecords, NULL) PHP_FE(dbase_numfields, NULL) PHP_FE(dbase_add_record, NULL) PHP_FE(dbase_replace_record, NULL) PHP_FE(dbase_get_record, NULL) PHP_FE(dbase_get_record_with_names, NULL) PHP_FE(dbase_delete_record, NULL) PHP_FE(dbase_pack, NULL) {NULL, NULL, NULL} }; zend_module_entry dbase_module_entry = { "dbase", dbase_functions, PHP_MINIT(dbase), PHP_MSHUTDOWN(dbase), NULL, NULL, NULL, STANDARD_MODULE_PROPERTIES }; #if defined(COMPILE_DL) || defined(COMPILE_DL_DBASE) ZEND_GET_MODULE(dbase) #if (WIN32|WINNT) && defined(THREAD_SAFE) /*NOTE: You should have an odbc.def file where you export DllMain*/ BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return 1; } #endif #endif #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */