This is expire.c in view mode; [Download] [Up]
/* * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved. * * Expire will remove all entries in the index and data files * corresponding to the articles before the first article registered * in the active file. No attempt is made to eliminate other * expired articles. */ #include "config.h" #include "db.h" #include "dir.h" import int trace, debug_mode; import int nntp_failed; /* * Expire methods: * 1: read directory and reuse database info. * 2: "slide" index and datafiles (may leave unexpired art. in database) * 3: recollect group to expire (also if "min" still exists) */ export int expire_method = 1; /* expire method */ export int recollect_method = 1; /* recollection method -- see do_expire */ export int expire_level = 0; /* automatic expiration detection */ #ifdef HAVE_DIRECTORY static article_number *article_list = NULL; static long art_list_length = 0; static sort_art_list(f1, f2) register article_number *f1, *f2; { return (*f1 < *f2) ? -1 : (*f1 == *f2) ? 0 : 1; } static article_number *get_article_list(dir) char *dir; { DIR *dirp; register Direntry *dp; register char c, *pp, *cp; register article_number *art; register long count = 0; /* No. of completions plus one */ if ((dirp = opendir(dir)) == NULL) return NULL; /* tough luck */ art = article_list; count = 0; while ((dp = readdir(dirp)) != NULL) { cp = dp->d_name; #ifdef FAKED_DIRECTORY if (dp->d_ino == 0) continue; cp[14] = NUL; #endif for (pp = cp; c = *pp++; ) if (!isascii(c) || !isdigit(c)) break; if (c) continue; if (count == art_list_length) { art_list_length += 250; article_list = resizeobj(article_list, article_number, art_list_length + 1); art = article_list + count; } *art++ = atol(cp); count++; } closedir(dirp); if (article_list != NULL) { *art = 0; if (count > 1) quicksort(article_list, count, article_number, sort_art_list); } return article_list; } static long expire_in_database(gh) register group_header *gh; { FILE *old, *data, *ix; off_t old_max_offset; register article_number *list; #ifdef NNTP extern article_number *nntp_get_article_list(); #endif article_number old_last_article; long count; if (gh->first_db_article > gh->last_db_article) return 0; if (gh->last_db_article == 0) return 0; if (!init_group(gh)) return 0; if (debug_mode == 1) { printf("\t\tExp %s (%ld..%ld)\r", gh->group_name, gh->first_db_article, gh->last_db_article); fl; } count = 0; old = data = ix = NULL; /* get list of currently available articles in the group */ #ifdef NNTP if (use_nntp) list = nntp_get_article_list(gh); else #endif list = get_article_list("."); if (list == NULL || *list == 0) { #ifdef NNTP if (nntp_failed == 2) { log_entry('N', "NNTP server supports neither LISTGROUP nor XHDR"); sys_error("Cannot use specified expire method (NNTP limitations)"); } if (nntp_failed) return -1; #endif if (debug_mode == 1) { printf("\rempty"); fl; } /* group is empty - clean it */ count = gh->last_db_article - gh->first_db_article + 1; clean_group(gh); gh->last_db_article = gh->last_a_article; gh->first_db_article = gh->last_db_article + 1; db_write_group(gh); return count; } /* * Clean & block the group while expire is working on it. */ gh->first_db_article = 0; old_last_article = gh->last_db_article; gh->last_db_article = 0; gh->index_write_offset = (off_t)0; old_max_offset = gh->data_write_offset; gh->data_write_offset = (off_t)0; gh->master_flag &= ~M_EXPIRE; gh->master_flag |= M_BLOCKED; db_write_group(gh); /* * We ignore the old index file, and we unlink the data file * immediately after open because we want to write a new. */ (void)open_data_file(gh, 'x', -1); old = open_data_file(gh, 'd', OPEN_READ | OPEN_UNLINK); if (old == NULL) goto out; data = open_data_file(gh, 'd', OPEN_CREATE | MUST_EXIST); ix = open_data_file(gh, 'x', OPEN_CREATE | MUST_EXIST); while (ftell(old) < old_max_offset) { if (s_hangup) { /* ok, this is what we got -- let collect get the rest */ old_last_article = gh->last_db_article; break; } /* * maybe not enough articles, or last one is incomplete * we take what there is, and leave the rest to do_collect() * It may actually be ok if the last articles have been expired/ * cancelled! */ if (db_read_art(old) <= 0) { break; } if (debug_mode == 1) { printf("\r%ld", (long)db_hdr.dh_number); fl; } /* check whether we want this article */ while (*list && db_hdr.dh_number > *list) { /* potentially, we have a problem here: there might be an */ /* article in the directory which is not in the database! */ /* For the moment we just ignore this - it might be a bad */ /* article which has been rejected!! */ list++; } if (*list == 0) { /* no more articles in the directory - the rest must be */ /* expired. So we ignore the rest of the data file */ break; } if (db_hdr.dh_number < *list) { /* the current article from the data file isn't in the */ /* article list, so it must be expired! */ count++; if (debug_mode == 1) { printf("\t%ld", count); fl; } continue; } if (gh->first_db_article == 0) { gh->first_db_article = db_hdr.dh_number; gh->last_db_article = db_hdr.dh_number - 1; } if (gh->last_db_article < db_hdr.dh_number) { gh->data_write_offset = ftell(data); /* must fill gab between last index and current article */ while (gh->last_db_article < db_hdr.dh_number) { if (!db_write_offset(ix, &(gh->data_write_offset))) write_error(); gh->last_db_article++; } } if (db_write_art(data) < 0) write_error(); } if (gh->first_db_article == 0) { gh->first_db_article = old_last_article + 1; gh->last_db_article = old_last_article; } else { gh->data_write_offset = ftell(data); while (gh->last_db_article < old_last_article) { /* must fill gab between last index and last article */ ++gh->last_db_article; if (!db_write_offset(ix, &(gh->data_write_offset))) write_error(); } gh->index_write_offset = ftell(ix); } gh->master_flag &= ~M_BLOCKED; db_write_group(gh); out: if (old) fclose(old); if (data) fclose(data); if (ix) fclose(ix); if (debug_mode) putchar(NL); return count; } #else #define expire_in_database expire_sliding #endif static long expire_sliding(gh) register group_header *gh; { FILE *old_x, *old_d; FILE *new; off_t index_offset, data_offset, new_offset; long count, expire_count; char *err_message; #define expire_error(msg) { \ err_message = msg; \ goto error_handler; \ } if (!init_group(gh)) return 0; old_x = old_d = new = NULL; #ifdef RENUMBER_DANGER /* * check whether new first article is collected */ if (!art_collected(gh, gh->first_a_article)) { expire_count = gh->first_db_article - gh->last_db_article + 1; err_message = NULL; goto error_handler; /* renumbering, collect from start */ } #else if (gh->first_a_article <= gh->first_db_article) return 0; #endif expire_count = gh->first_a_article - gh->first_db_article; new = NULL; /* * Open old files, unlink after open */ old_x = open_data_file(gh, 'x', OPEN_READ|OPEN_UNLINK); old_d = open_data_file(gh, 'd', OPEN_READ|OPEN_UNLINK); if (old_x == NULL || old_d == NULL) expire_error("INDEX or DATA file missing"); /* * Create new index file; copy from old */ new = open_data_file(gh, 'x', OPEN_CREATE); if (new == NULL) expire_error("INDEX: cannot create"); /* * index_offset is the offset into the old index file for the * first entry in the new index file */ index_offset = get_index_offset(gh, gh->first_a_article); /* * adjust the group's index write offset (the next free entry) */ gh->index_write_offset -= index_offset; /* * calculate the number of entries to copy */ count = gh->index_write_offset / sizeof(off_t); /* * data offset is the offset into the old data file for the * first byte in the new data file; it is initialized in the * loop below, by reading the entry in the old index file at * offset 'index_offset'. */ data_offset = (off_t)0; /* * read 'count' entries from the old index file starting from * index_offset, subtract the 'data_offset', and output the * new offset to the new index file. */ fseek(old_x, index_offset, 0); while (--count >= 0) { if (!db_read_offset(old_x, &new_offset)) expire_error("INDEX: too short"); if (data_offset == (off_t)0) data_offset = new_offset; new_offset -= data_offset; if (!db_write_offset(new, &new_offset)) expire_error("NEW INDEX: cannot write"); } fclose(new); fclose(old_x); old_x = NULL; /* * copy from old data file to new data file */ new = open_data_file(gh, 'd', OPEN_CREATE); if (new == NULL) expire_error("DATA: cannot create"); /* * calculate offset for next free entry in the new data file */ gh->data_write_offset -= data_offset; /* * calculate number of bytes to copy (piece of cake) */ count = gh->data_write_offset; /* * copy 'count' bytes from the old data file, starting at offset * 'data_offset', to the new data file */ fseek(old_d, data_offset, 0); while (count > 0) { char block[1024]; int count1; count1 = fread(block, sizeof(char), 1024, old_d); if (count1 <= 0) expire_error("DATA: read error"); if (fwrite(block, sizeof(char), count1, new) != count1) expire_error("DATA: write error"); count -= count1; } fclose(new); fclose(old_d); /* * Update group entry */ gh->first_db_article = gh->first_a_article; /* * Return number of expired articles */ return expire_count; /* * Errors end up here. * We simply recollect the whole group once more. */ error_handler: if (new) fclose(new); if (old_x) fclose(old_x); if (old_d) fclose(old_d); if (err_message) log_entry('E', "Expire Error (%s): %s", gh->group_name, err_message); clean_group(gh); /* will be saved & unblocked later */ /* * We cannot say whether any articles actually had to be expired, * but then we must guess... */ return expire_count; } static block_group(gh) register group_header *gh; { if ((gh->master_flag & M_BLOCKED) == 0) { gh->master_flag |= M_BLOCKED; db_write_group(gh); } } static unblock_group(gh) register group_header *gh; { if (gh->master_flag & M_BLOCKED) { gh->master_flag &= ~(M_BLOCKED | M_EXPIRE); db_write_group(gh); } } do_expire() { register group_header *gh; long exp_article_count, temp; int exp_group_count, must_expire; time_t start_time; must_expire = 0; Loop_Groups_Header(gh) { if (s_hangup) break; if (gh->master_flag & M_IGNORE_GROUP) continue; if ((gh->master_flag & M_VALID) == 0) { log_entry('X', "Group %s removed", gh->group_name); gh->master_flag |= M_IGNORE_A; clean_group(gh); continue; } #ifdef RENUMBER_DANGER if (gh->last_db_article > gh->last_a_article || gh->first_db_article > gh->first_a_article) { log_entry('X', "group %s renumbered", gh->group_name); clean_group(gh); continue; } #endif if (gh->master_flag & M_AUTO_RECOLLECT) { switch (recollect_method) { case 1: /* expire when new articles arrive */ if (gh->last_a_article <= gh->last_db_article) break; case 2: /* expire unconditionally */ gh->master_flag |= M_EXPIRE; must_expire = 1; continue; case 3: /* clean when new articles arrive */ if (gh->last_a_article <= gh->last_db_article) break; case 4: /* clean unconditionally */ gh->master_flag |= M_MUST_CLEAN; continue; default: /* ignore auto-recollect */ break; } } if (gh->index_write_offset > 0) { if (gh->first_a_article > gh->last_db_article) { if (trace) log_entry('T', "%s expire void", gh->group_name); if (debug_mode) printf("%s expire void\n", gh->group_name); clean_group(gh); continue; } } if (gh->master_flag & M_EXPIRE) { must_expire = 1; continue; } if (expire_level > 0 && (gh->first_db_article + expire_level) <= gh->first_a_article) { if (trace) log_entry('T', "%s expire level", gh->group_name); if (debug_mode) printf("Expire level: %s\n", gh->group_name); gh->master_flag |= M_EXPIRE; must_expire = 1; continue; } } if (!must_expire) return 1; start_time = cur_time(); exp_article_count = exp_group_count = 0; temp = 0; Loop_Groups_Header(gh) { if (s_hangup) { temp = -1; break; } if (gh->master_flag & M_IGNORE_GROUP) continue; if ((gh->master_flag & M_EXPIRE) == 0) continue; if (gh->master_flag & M_MUST_CLEAN) continue; if (trace) log_entry('T', "Exp %s (%ld -> %ld)", gh->group_name, (long)gh->first_db_article, (long)gh->first_a_article); switch (expire_method) { case 1: block_group(gh); temp = expire_in_database(gh); unblock_group(gh); break; case 2: block_group(gh); temp = expire_sliding(gh); unblock_group(gh); break; case 4: temp = gh->first_a_article - gh->first_db_article; if (temp <= 0) break; case 3: gh->master_flag |= M_MUST_CLEAN; break; } #ifdef NNTP if (nntp_failed) { /* connection broken while reading article list */ /* so no harm was done - leave the group to be expired */ /* again on next expire */ break; } #endif if (temp > 0) { exp_article_count += temp; exp_group_count++; } } if (exp_article_count > 0) log_entry('X', "Expire: %ld art, %d gr, %ld s", exp_article_count, exp_group_count, (long)(cur_time() - start_time)); return temp >= 0; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.