• Patch: Elm ME+ 2.5 PLalpha63 -> Elm ME+ 2.5 PLalpha64 [2/4] (2/5)

    From Kari Hurtta@21:1/5 to All on Tue Jul 2 20:52:17 2024
    [continued from previous message]

    + entry_same_file =
    + ( isoff(entry->entry_flags,LAST_READ_have_st_ino) &&
    + isoff(entry_flags,LAST_READ_have_st_ino) )
    + ||
    + ( ison(entry->entry_flags,LAST_READ_have_st_ino) &&
    + ison(entry_flags,LAST_READ_have_st_ino) &&
    + st_ino == entry->st_ino );
    + }
    +
    + if ((time_t) -1 != now && last_read > now) {
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: %Q: %zu -- timestamp in future, last_read = %d > now = %d\n",
    + filename,lineno,entry_name, idx,
    + last_read,now));
    +
    + } else if (ison(entry->entry_flags,LAST_READ_old) ||
    + isoff(entry->entry_flags,LAST_READ_valid) || +
    + ( entry_same_file &&
    + entry->last_read < last_read) ||
    +
    + ( isoff(entry->entry_flags,LAST_READ_modified) &&
    + entry->last_read < last_read)
    + ) {
    +
    + /* Update from disk */
    +
    + entry->entry_flags = entry_flags |
    + ( entry->entry_flags &
    + LAST_READ_flag );
    +
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: %Q: %zu -- %s",
    + filename,lineno,entry_name, idx,
    + ison(entry->entry_flags,LAST_READ_valid) ?
    + "updating" : "setting"));
    +
    + if (ison(entry->entry_flags,LAST_READ_valid)) {
    + if (entry->last_read != last_read) {
    + DPRINT(Debug,14,(&Debug, " last read = %d -> %d",
    + entry->last_read,last_read));
    + }
    +
    + if (ison(entry_flags,LAST_READ_have_st_ino) && + entry->st_ino != st_ino) {
    + DPRINT(Debug,14,(&Debug, " st_ino = %lu -> %lu",
    + (unsigned long)entry->st_ino,
    + (unsigned long)st_ino));
    + }
    + } else {
    + DPRINT(Debug,14,(&Debug, " last read = %d",
    + last_read));
    +
    + if (ison(entry_flags,LAST_READ_have_st_ino)) { + DPRINT(Debug,14,(&Debug, " st_ino = %lu",
    + (unsigned long)st_ino));
    + }
    + }
    +
    + if (comment_len > 0) {
    + comment[comment_len] = '\0';
    +
    + DPRINT(Debug,14,(&Debug, " comment = %.*s",
    + comment_len,comment));
    + }
    +
    + DPRINT(Debug,14,(&Debug,"\n"));
    +
    + entry->last_read = last_read;
    + entry->st_ino = st_ino;
    +
    + } else {
    +
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: %Q: %zu -- skipping update from disk, %s\n",
    + filename,lineno,entry_name, idx,
    + entry_same_file ? "is same file" : "not same file"));
    + }
    +
    + /* lineno is incremented later */
    + } else {
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: %Q: newline expected\n",
    + filename,lineno,entry_name));
    +
    + if (suggest_rewrite)
    + (*suggest_rewrite)++;
    +
    + goto skip_line;
    + }
    +
    + } else {
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: %Q: Skipping line\n",
    + filename,lineno,entry_name));
    +
    + skip_line:
    +
    + while (EOF != c && '\n' != c)
    + c = fgetc(f);
    +
    + }
    +
    + /* Decrements refcount */
    + free_last_read_entry(& res.last_read_entry);
    +
    +
    + } else {
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: %Q: search_sort_list_item did not set last_read_entry\n",
    + filename,lineno,entry_name));
    + goto fail_line;
    + }
    +
    + } else {
    +
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: %Q: search_sort_list_item failed to create item\n",
    + filename,lineno,entry_name));
    +
    + fail_line:
    + if (suggest_rewrite)
    + (*suggest_rewrite)++;
    +
    + end_line:
    + while (EOF != c && '\n' != c)
    + c = fgetc(f);
    + }
    +
    + switch (c) {
    + case EOF:
    +
    + if (feof(f)) {
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: Unexpect end of file\n",
    + filename,lineno));
    + }
    +
    + if (ferror(f)) {
    + int err UNUSED_VAROK = errno;
    +
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %d: %s\n",
    + filename,lineno,strerror(err)));
    + }
    + break;
    +
    + case '\n':
    + lineno++;
    + break;
    + }
    + }
    +
    + if (entry_name) {
    + free(entry_name);
    + entry_name = NULL;
    + }
    + entry_name_alloced = 0;
    +
    + if (cache_file) {
    +
    + if (! stat_last_read_cache(filename,f,cache_file)) {
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: stat %s failed\n",
    + filename));
    + ret = 0;
    + goto fail;
    + }
    + }
    +
    + cache_len = sort_list_len(* cache_entries);
    +
    + if (feof(f)) {
    +
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %zu items\n",
    + filename,cache_len));
    +
    + if (cache_len > 0) {
    + size_t i = cache_len -1;
    + size_t deleted = 0;
    + size_t valid = 0;
    + size_t purge = 0;
    + size_t modified = 0;
    +
    + while (1) {
    + union sort_item res;
    +
    + res.last_read_entry = NULL;
    +
    + /* Increments refcount */
    + get_sort_list_item(* cache_entries,sort_list_get_normal,i,
    + &res);
    +
    + if (res.last_read_entry) {
    + struct last_read_entry * entry;
    +
    + if (LAST_READ_ENTRY_magic != res.last_read_entry->magic)
    + panic("MBX PANIC",__FILE__,__LINE__,
    + "parse_last_read_cache",
    + "Bad magic type (last_read_entry)",0);
    +
    + entry = res.last_read_entry;
    +
    + if (ison(entry->entry_flags,LAST_READ_purge))
    + purge++;
    + if (ison(entry->entry_flags,LAST_READ_valid))
    + valid++;
    + if (ison(entry->entry_flags,LAST_READ_modified))
    + modified++;
    +
    + if (isoff(entry->entry_flags,LAST_READ_valid) ||
    + (ison(entry->entry_flags,LAST_READ_purge) &&
    + isoff(entry->entry_flags,LAST_READ_modified))) {
    +
    + free_last_read_entry(& res.last_read_entry);
    +
    + /* Increments refcount */
    + get_sort_list_item(* cache_entries,sort_list_get_remove,i,
    + &res);
    +
    + if (res.last_read_entry) {
    + struct last_read_entry * entry;
    +
    + if (LAST_READ_ENTRY_magic != res.last_read_entry->magic)
    + panic("MBX PANIC",__FILE__,__LINE__,
    + "parse_last_read_cache",
    + "Bad magic type (last_read_entry)",0);
    +
    + entry = res.last_read_entry;
    +
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: #%zu folder_sys=%s - deleted\n",
    + i,entry->folder_sys));
    +
    +
    + deleted++;
    + }
    +
    +
    +
    + } else
    + clearit(entry->entry_flags,LAST_READ_purge);
    +
    + if (res.last_read_entry)
    + free_last_read_entry(& res.last_read_entry);
    + }
    +
    + if (i > 0)
    + i--;
    + else
    + break;
    + }
    +
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: %zu valid, %zu flagged purge, %zu modified items - %zu deleted.\n",
    + filename,valid,purge,modified,deleted));
    + }
    +
    + ret = 1;
    + } else {
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache: %s: All items not readed\n",
    + filename));
    + }
    +
    + fail:
    + DPRINT(Debug,14,(&Debug,
    + "parse_last_read_cache=%d\n",
    + ret));
    +
    + return ret;
    + }
    +
    +
    +
    + static enum quote_name_res {
    + quote_name_bad = -1 /* Bad name */,
    + quote_name_no = 0 /* do not quote */,
    + quote_name_needed
    +
    + } quote_name P_((const char *folder_sys));
    + static enum quote_name_res quote_name(folder_sys)
    + const char *folder_sys;
    + {
    + const char * c;
    +
    + enum quote_name_res ret = quote_name_no;
    +
    +
    + if (! folder_sys[0])
    + return quote_name_bad;
    +
    + if ('#' == folder_sys[0])
    + ret = quote_name_needed;
    +
    + for (c = folder_sys; *c; c++) {
    +
    + switch (*c) {
    +
    + case '\n':
    + return quote_name_bad;
    +
    + case '"':
    + case '\\':
    + ret = quote_name_needed;
    + break;
    +
    + case '\r':
    + return quote_name_bad;
    +
    + case '/':
    +
    + /* / is only allowed on absolute paths */
    +
    + if ('/' != folder_sys[0])
    + return quote_name_bad;
    +
    + break;
    +
    + default:
    + if (whitespace(*c))
    + ret = quote_name_needed;
    +
    + break;
    + }
    +
    + if (c - folder_sys >= NAME_LIMIT)
    + return quote_name_bad;
    + }
    +
    + return ret;
    + }
    +
    +
    + /* This is written to temporary file */
    + static int dump_last_read_cache P_((FILE *f,
    + const char * filename /* temp file name */,
    + struct file_changes * cache_file,
    + struct sortlist * cache_entries,
    + int * errno_res,
    +
    + /* May be NULL */
    + FILE *commentfile,
    + const char *actor,
    + char *version_buff
    + ));
    + static int dump_last_read_cache(f,filename,cache_file,cache_entries,errno_res,
    + commentfile,actor,version_buff)
    + FILE *f;
    + const char * filename /* temp file name */;
    + struct file_changes * cache_file;
    + struct sortlist * cache_entries;
    + int * errno_res;
    +
    + /* May be NULL */
    + FILE * commentfile;
    + const char * actor;
    + char * version_buff;
    + {
    + int ret = 0;
    + size_t cache_len = 0;
    + int r;
    + size_t z1;
    +
    + static char actor1[SLEN] = "";
    + static char version1[SLEN] = "";
    +
    + if (actor && version_buff) {
    + strfcpy(actor1,actor,sizeof actor1);
    + strfcpy(version1,version_buff,sizeof version1);
    + } else if (actor1[0]) { /* Sticky */
    + actor = &(actor1[0]);
    + version_buff = &(version1[0]);
    + }
    +
    + if (! cache_entries) {
    + ret = 0;
    +
    + goto fail;
    + }
    +
    + cache_len = sort_list_len(cache_entries);
    +
    + if ((z1=fwrite(LAST_READ_HEADER,1, LAST_READ_HEADER_len,f)) <
    + LAST_READ_HEADER_len) {
    +
    + if (ferror(f)) {
    + int err = errno;
    +
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: fwrite failed: %s\n",
    + filename,strerror(err)));
    +
    + lib_error(CATGETS(elm_msg_cat, MeSet,
    + MeLastReadWriteTempFail,
    + "Write failed for last-read temp file %s: %s"),
    + filename,strerror(err));
    +
    + if (errno_res)
    + *errno_res = err;
    + } else {
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: short fwrite = %zu < %zu\n",
    + filename,z1,LAST_READ_HEADER_len));
    + }
    +
    + ret = 0;
    +
    + goto fail;
    + }
    +
    + /* commentfile,actor,version_buff may be NULL */
    + insert_commentfile(f,ELMLASTREAD_INFO,commentfile,
    + actor,version_buff);
    +
    + if (cache_len > 0) {
    + size_t i;
    +
    + size_t need_delete = 0;
    +
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: cache_len=%zu\n",
    + filename,cache_len));
    +
    + for (i = 0; i < cache_len; i++) {
    +
    + union sort_item res;
    +
    + res.last_read_entry = NULL;
    +
    + /* Increments refcount */
    + get_sort_list_item(cache_entries,sort_list_get_normal,i,
    + &res);
    +
    + if (res.last_read_entry) {
    + struct last_read_entry * entry;
    + enum quote_name_res q;
    + char * X;
    +
    + if (LAST_READ_ENTRY_magic != res.last_read_entry->magic)
    + panic("MBX PANIC",__FILE__,__LINE__,
    + "dump_last_read_cache",
    + "Bad magic type (last_read_entry)",0);
    +
    + entry = res.last_read_entry;
    +
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: entry %zu = %Q",
    + i,entry->folder_sys));
    +
    + DPRINT(Debug,14,(&Debug,", entry_flags=%u %s",
    + entry->entry_flags,
    + give_entry_flags(entry->entry_flags)
    + ));
    +
    + if (ison(entry->entry_flags,
    + LAST_READ_delete)) {
    + need_delete++;
    +
    + DPRINT(Debug,14,(&Debug," -- delete, skipping\n"));
    + goto skip_this_entry;
    + }
    +
    + if (isoff(entry->entry_flags,
    + LAST_READ_valid)) {
    + DPRINT(Debug,14,(&Debug," -- not valid, skipping\n"));
    + goto skip_this_entry;
    + }
    +
    + if (ison(entry->entry_flags,
    + LAST_READ_memonly)) {
    + DPRINT(Debug,14,(&Debug," -- mem only, skipping\n"));
    + goto skip_this_entry;
    + }
    +
    + q = quote_name(entry->folder_sys);
    + DPRINT(Debug,14,(&Debug," quote name = %d",
    + q));
    + switch (q) {
    + case quote_name_bad:
    + DPRINT(Debug,14,(&Debug," quote_name_bad")); break;
    + case quote_name_no:
    + DPRINT(Debug,14,(&Debug," quote_name_no")); break;
    + case quote_name_needed:
    + DPRINT(Debug,14,(&Debug," quote_name_needed")); break;
    + }
    +
    + if (q <= quote_name_bad) {
    + DPRINT(Debug,14,(&Debug,", skipping\n"));
    + setit(entry->entry_flags,LAST_READ_memonly);
    + goto skip_this_entry;
    + } else if (q > quote_name_no) {
    + DPRINT(Debug,14,(&Debug,", quoting\n"));
    +
    + elm_fprintf(f,FRM("%Q"),entry->folder_sys);
    + } else {
    + DPRINT(Debug,14,(&Debug,"\n"));
    +
    + fputs(entry->folder_sys,f);
    + }
    +
    + /* Both space and TAB allowed */
    + putc('\t',f);
    +
    + if (ison(entry->entry_flags,
    + LAST_READ_have_st_ino)) {
    +
    + fprintf(f,"%lu",(unsigned long)(entry->st_ino));
    +
    + } else
    + putc('-',f); /* Placeholder */
    +
    + /* Both space and TAB allowed */
    + putc('\t',f);
    +
    + fprintf(f,"%ld",(long)(entry->last_read));
    +
    + X = ctime(&(entry->last_read));
    +
    + /* ctime() includes newline \n to result */
    + if (X) {
    + fprintf(f,"\t# %s",
    + X);
    +
    + } else
    + putc('\n',f);
    +
    + skip_this_entry:
    + free_last_read_entry(& res.last_read_entry);
    +
    + }
    + }
    +
    +
    + if (ferror(f)) {
    + int err = errno;
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: write failed: %s\n",
    + filename,strerror(err)));
    +
    + lib_error(CATGETS(elm_msg_cat, MeSet,
    + MeLastReadWriteTempFail,
    + "Write failed for last-read temp file %s: %s"),
    + filename,strerror(err));
    +
    + if (errno_res)
    + *errno_res = err;
    +
    + ret = 0;
    + goto fail;
    + }
    +
    + r = fflush(f);
    + switch(r) {
    + int err;
    + case 0:
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: written\n",
    + filename));
    + ret = 1;
    + break;
    + case EOF:
    + err = errno;
    +
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: flush failed: %s\n",
    + filename,strerror(err)));
    +
    + lib_error(CATGETS(elm_msg_cat, MeSet,
    + MeLastReadWriteTempFail,
    + "Write failed for last-read temp file %s: %s"),
    + filename,strerror(err));
    +
    + if (errno_res)
    + *errno_res = err;
    +
    + ret = 0;
    + goto fail;
    + }
    +
    + if (cache_file) {
    + if (! stat_last_read_cache(filename,f,cache_file)) {
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: stat %s failed\n",
    + filename));
    + ret = 0;
    + goto fail;
    + }
    + }
    +
    + if (need_delete > 0) {
    +
    + size_t i = cache_len -1;
    + size_t deleted = 0;
    + size_t valid = 0;
    +
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: %zu entries marked for delete\n",
    + filename,need_delete));
    +
    + while (1) {
    + union sort_item res;
    +
    + res.last_read_entry = NULL;
    +
    + /* Increments refcount */
    + get_sort_list_item(cache_entries,sort_list_get_normal,i,
    + &res);
    +
    + if (res.last_read_entry) {
    + struct last_read_entry * entry;
    +
    + if (LAST_READ_ENTRY_magic != res.last_read_entry->magic)
    + panic("MBX PANIC",__FILE__,__LINE__,
    + "dump_last_read_cache",
    + "Bad magic type (last_read_entry)",0);
    +
    + entry = res.last_read_entry;
    +
    + if (ison(entry->entry_flags,LAST_READ_valid))
    + valid++;
    +
    + if (ison(entry->entry_flags,LAST_READ_delete)) {
    +
    + free_last_read_entry(& res.last_read_entry);
    +
    + /* Increments refcount */
    + get_sort_list_item(cache_entries,sort_list_get_remove,i,
    + &res);
    +
    + if (res.last_read_entry) {
    + struct last_read_entry * entry;
    +
    + if (LAST_READ_ENTRY_magic != res.last_read_entry->magic)
    + panic("MBX PANIC",__FILE__,__LINE__,
    + "dump_last_read_cache",
    + "Bad magic type (last_read_entry)",0);
    +
    + entry = res.last_read_entry;
    +
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: #%zu folder_sys=%s - deleted\n",
    + i,entry->folder_sys));
    +
    +
    + deleted++;
    + }
    + }
    +
    + if (res.last_read_entry)
    + free_last_read_entry(& res.last_read_entry);
    + }
    +
    + if (i > 0)
    + i--;
    + else
    + break;
    + }
    +
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: %zu valid, %zu deleted.\n",
    + filename,valid,deleted));
    + }
    +
    + } else {
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache: %s: No entries\n",
    + filename
    + ));
    + }
    +
    +
    +
    + fail:
    +
    + DPRINT(Debug,14,(&Debug,
    + "dump_last_read_cache=%d%s; filename=%s\n",
    + ret,
    + ret ? " (succeed)" : "",
    + filename));
    + return ret;
    + }
    +
    +
    +
    + static struct last_read_cache *malloc_last_read_cache(sys_dir,st,
    + prealloc,fchanges,
    + filename)
    + const char * sys_dir;
    + const struct stat * st;
    + size_t prealloc;
    + const struct file_changes * fchanges;
    + const char * filename;
    + {
    + struct last_read_cache * ret =
    + safe_zero_alloc(sizeof (*ret));
    +
    + ret -> magic = LAST_READ_CACHE_magic;
    +
    + ret -> refcount = 1;
    +
    + ret -> sys_dir = sys_dir ? safe_strdup(sys_dir) : NULL;
    + ret -> cached_name = NULL;
    +
    +
    + if (filename) {
    + ret -> cache_file_name = safe_strdup(filename);
    + } else
    + ret -> cache_file_name = NULL; /* Filled later */
    +
    + ret->cache_file = NULL_file_changes;
    +
    + if (fchanges)
    + ret->cache_file = *fchanges;
    +
    + if (st)
    + stat_to_file_changes(st,& (ret->cache_file));
    +
    + ret->entries = alloc_sort_list(&last_read_entry_op,
    + prealloc);
    +
    + ret->modified = 0;
    + ret->read = 0;
    +
    + return ret;
    + }
    +
    + enum last_read_mode {
    + last_read_read_only = 0,
    + last_read_check,
    + last_read_read_update /* opended for rewrite */,
    + last_read_create /* Create for locking */
    + };
    +
    +
    + enum LREAD_OPEN_bits {
    + LREAD_OPEN_bit_SUGGEST_REWRITE,
    + LREAD_OPEN_bit_FILE_FD_LOCKED,
    + LREAD_OPEN_bit_WRITTEN,
    +
    + LREAD_OPEN_bit_count
    + };
    +
    + #define LREAD_OPEN_SUGGEST_REWRITE (1 << LREAD_OPEN_bit_SUGGEST_REWRITE)
    + #define LREAD_OPEN_FILE_FD_LOCKED (1 << LREAD_OPEN_bit_FILE_FD_LOCKED)
    + #define LREAD_OPEN_WRITTEN (1 << LREAD_OPEN_bit_WRITTEN)
    +
    +
    + #define LAST_READ_OPEN_magic 0xFA0E
    +
    + static struct last_read_open {
    + unsigned short magic; /* LAST_READ_OPEN_magic */
    +
    + int file_fd;
    + FILE * fh;
    +
    + const char * filename;
    + struct dt_flags_info * locking_flags;
    +
    + enum last_read_mode is_update;
    +
    + unsigned int lread_open_flags : LREAD_OPEN_bit_count;
    +
    + } * open_last_read_cache P_((const char * filename,
    + struct dt_flags_info * locking_flags,
    + enum last_read_mode is_update,
    + struct file_changes * file_information,
    + int * errno_res
    + ));
    +
    + static struct last_read_open * open_last_read_cache(filename,
    + locking_flags,
    + is_update,
    + file_information,
    + errno_res)
    + const char * filename;
    + struct dt_flags_info * locking_flags;
    + enum last_read_mode is_update;
    + struct file_changes * file_information;
    + int * errno_res;
    + {
    + struct last_read_open * ret = NULL;
    +
    + enum FLOCKING_mode fm = FLOCKING_exclusive;
    + char * openm = "r+";
    + int accm = O_RDWR;
    + mode_t creatm = 0600; /* Supposed to be read only
    + by user */
    +
    + int file_fd = -1;
    + int file_fd_locked = 0;
    + int loop_count = 0;
    + int loop_message_printed = 0;
    +
    + FILE * f = NULL;
    +
    + struct stat file_fd_stat;
    + struct stat filename_stat;
    +
    + int have_file_fd_stat = 0;
    + int have_filename_stat = 0;
    + int have_creat_retry = 0;
    +
    +
    + DPRINT(Debug,10,(&Debug,
    + "open_last_read_cache: is_update=%d",
    + is_update));
    +
    + switch (is_update) {
    + case last_read_read_only:
    + DPRINT(Debug,10,(&Debug," last_read_read_only"));
    + fm = FLOCKING_shared;
    + openm = "r";
    + accm = O_RDONLY;
    +
    + break;
    +
    + case last_read_check:
    + DPRINT(Debug,10,(&Debug," last_read_check"));
    +
    + if (0) {
    + case last_read_read_update:
    + DPRINT(Debug,10,(&Debug," last_read_read_update"));
    + }
    + fm = FLOCKING_exclusive;
    + openm = "r+";
    + accm = O_RDWR;
    + break;
    +
    + case last_read_create:
    + DPRINT(Debug,10,(&Debug," last_read_create"));
    + fm = FLOCKING_exclusive;
    + openm = "r+";
    + accm = O_RDWR;
    + break;
    + }
    + DPRINT(Debug,10,(&Debug," filename=%Q\n",
    + filename));
    +
    + while (-1 == file_fd) {
    + enum syscall_status r;
    + enum FLOCKING_status r1;
    +
    + int err = can_open(filename,openm);
    + int creatf = 0;
    +
    + file_fd_locked = 0;
    +
    + if (err) {
    + DPRINT(Debug,2,(&Debug,
    + "open_last_read_cache: %s: %s (can_open)\n",
    + filename,strerror(err)));
    +
    + if (EINTR == err) {
    + continue;
    + }
    +
    + if (ENOENT == err &&
    + last_read_create == is_update &&
    + ! have_creat_retry) {
    +
    + DPRINT(Debug,10,(&Debug,
    + "open_last_read_cache: %s: Trying create file\n",
    + filename));
    +
    + creatf = O_CREAT|O_EXCL;
    + } else {
    + if (loop_message_printed) {
    + lib_transient(CATGETS(elm_msg_cat, MeSet,
    + MeLastReadWaitingFail,
    + "Waiting last-read file %s to be updated... Fail: %s"),
    + filename,strerror(err));
    + }
    +
    + if (errno_res)
    + *errno_res = err;
    +
    + return NULL;
    + }
    +
    + } else {
    + have_creat_retry = 0;
    + }
    +
    + file_fd = open(filename,accm|creatf,creatm);
    +
    + if (-1 == file_fd) {
    + err = errno;
    +
    + DPRINT(Debug,10,(&Debug,"open_last_read_cache: %s: %s\n",
    + filename,strerror(err)));
    +
    + if (EINTR == err) {
    + continue;
    + }
    +
    + if (EEXIST == err) {
    + /* Must have create failure
    + -- retry with canopen()
    + */
    +
    + DPRINT(Debug,10,(&Debug,
    + "open_last_read_cache: %s: Trying open file\n",
    + filename));
    +
    + have_creat_retry = 1;
    + continue;
    + }
    +
    + if (loop_message_printed) {
    + lib_transient(CATGETS(elm_msg_cat, MeSet,
    + MeLastReadWaitingFail,
    + "Waiting last-read file %s to be updated... Fail: %s"),
    + filename,strerror(err));
    + }
    +
    + if (errno_res)
    + *errno_res = err;
    +
    + return NULL;
    + }
    +
    + r = fstat(file_fd,&file_fd_stat);
    + switch (r) {
    + char *X;
    + case syscall_success:
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: fstat %d: %s succeed: dev %lu ino %lu size %ld modified %ld",
    + file_fd,filename,
    + (unsigned long)file_fd_stat.st_dev,
    + (unsigned long)file_fd_stat.st_ino,
    + (long)file_fd_stat.st_size,
    + (long)file_fd_stat.st_mtime));
    +
    + X = ctime(& (file_fd_stat.st_mtime));
    + if (X) { /* ctime() includes newline */
    + DPRINT(Debug,14,(&Debug," -- %s",X));
    + } else {
    + DPRINT(Debug,14,(&Debug,"\n"));
    + }
    +
    + have_file_fd_stat = 1;
    +
    + break;
    + case syscall_error:
    + err = errno;
    +
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: fstat %d: %s failed: %s\n", + file_fd,filename,strerror(err)));
    +
    + if (EINTR == err) {
    + goto oops;
    + }
    +
    + /* Just read old file ? */
    + goto quit_locking_loop;
    + }
    +
    + /* <last_read> is locked with exclusive lock
    + when new file <last_read>.N is written
    + and then renamed to <last_read>
    +
    + */
    +
    + r1 = filelock_fd(file_fd,fm,
    + locking_flags,
    + filename,
    + FLOCKING_non_block,
    + &err);
    +
    + switch (r1) {
    + case FLOCKING_FAIL:
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: filelock_fd %d: %s locking failed: %s\n",
    + file_fd,filename,strerror(err)));
    +
    + if (errno_res)
    + *errno_res = err;
    +
    + /* Just read old file ? */
    + goto quit_locking_loop;
    + case FLOCKING_OK:
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: filelock_fd %d: %s locked\n",
    + file_fd,filename));
    + file_fd_locked = 1;
    + break;
    + case FLOCKING_RETRY:
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: filelock_fd %d: %s locking failed (retry)",
    + file_fd,filename));
    + if (err) {
    + DPRINT(Debug,14,(&Debug,": %s",
    + strerror(err)));
    + }
    + DPRINT(Debug,14,(&Debug,"\n"));
    +
    + if (EINTR == err) {
    + goto oops;
    + }
    +
    + goto wait_to_change;
    + case FLOCKING_UNSUPPORTED:
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: filelock_fd %d: %s locking not supported\n",
    + file_fd,filename));
    + /* Just read old file ? */
    + goto quit_locking_loop;
    + }
    +
    + r = stat(filename,&filename_stat);
    + switch (r) {
    + char *X;
    + case syscall_success:
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: stat %s succeed: dev %lu ino %lu size %ld modified %ld",
    + filename,
    + (unsigned long)filename_stat.st_dev,
    + (unsigned long)filename_stat.st_ino,
    + (long)filename_stat.st_size,
    + (long)filename_stat.st_mtime));
    + X = ctime(& (filename_stat.st_mtime));
    + if (X) { /* ctime() includes newline */
    + DPRINT(Debug,14,(&Debug," -- %s",X));
    + } else {
    + DPRINT(Debug,14,(&Debug,"\n"));
    + }
    +
    + have_filename_stat = 1;
    +
    + break;
    + case syscall_error:
    + err = errno;
    +
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: stat %s: %s\n",
    + filename,strerror(err)));
    + if (EINTR == err) {
    + goto oops;
    + }
    +
    + /* Just read old file ? */
    + goto quit_locking_loop;
    + }
    +
    + if (!have_filename_stat || !have_file_fd_stat) {
    +
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: file %s - no stat?\n",
    + filename));
    +
    + goto quit_locking_loop;
    +
    + } else if (filename_stat.st_dev == file_fd_stat.st_dev &&
    + filename_stat.st_ino == file_fd_stat.st_ino &&
    + filename_stat.st_size == file_fd_stat.st_size &&
    + filename_stat.st_mtime == file_fd_stat.st_mtime) {
    +
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: file %s not changed (since open)\n",
    + filename));
    +
    + if (loop_message_printed) {
    + lib_transient(CATGETS(elm_msg_cat, MeSet,
    + MeLastReadWaitingOK,
    + "Waiting last-read file %s to be updated... OK"),
    + filename);
    + loop_message_printed = 0;
    + }
    +
    + goto quit_locking_loop;
    +
    + } else {
    + int wait_it;
    +
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: file %s changed after open\n",
    + filename));
    +
    + oops:
    + wait_it = 0;
    +
    + if (0) {
    + wait_to_change:
    + wait_it = 1;
    + }
    +
    + if (loop_count++ > 10) {
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: file %s - try #%d -- quiting\n",
    + filename,loop_count));
    + goto quit_locking_loop;
    + }
    +
    + if (wait_it) {
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: file %s - try #%d -- waiting\n",
    + filename,loop_count));
    +
    + if (!loop_message_printed) {
    + lib_transient(CATGETS(elm_msg_cat, MeSet,
    + MeLastReadWaiting,
    + "Waiting last-read file %s to be updated..."),
    + filename);
    + loop_message_printed = 1;
    + }
    +
    + if (POLL_method)
    + wait_for_timeout(5);
    + else
    + sleep(5);
    +
    + } else {
    + DPRINT(Debug,14,(&Debug,
    + "open_last_read_cache: file %s - try #%d -- looping\n",
    + filename,loop_count));
    + }
    +
    + if (file_fd_locked) { /* Just ignore error --
    + close() should release this anyway
    + */
    + filelock_fd(file_fd,FLOCKING_release,locking_flags,
    + filename,FLOCKING_non_block,NULL);
    + }
    +
    + close(file_fd); /* Result ignored */
    + file_fd = -1;
    + }
    + }
    +
    + quit_locking_loop:
    +
    + if (loop_message_printed) {
    + lib_transient(CATGETS(elm_msg_cat, MeSet,
    + MeLastReadWaitingError,
    + "Waiting last-read file %s to be updated... Error"),
    + filename);
    + }
    +
    + if (file_information) {
    + if (FILE_CHANGES_magic != file_information->magic)
    + panic("MBX PANIC",__FILE__,__LINE__,"open_last_read_cache",
    + "Bad magic number (file_changes)",0);
    +
    + if (sizeof (*file_information) != file_information->self_size) {
    +
    + DPRINT(Debug,1,(&Debug,
    + "open_last_read_cache: size mismatch file_changes size %zu != self size %zu\n",
    + sizeof (*file_information),file_information->self_size));
    +
    + } else {
    + stat_to_file_changes(have_file_fd_stat ? &file_fd_stat : NULL,
    + file_information);
    + }
    + }
    +
    + f = fdopen(file_fd,openm);
    + if (!f) {
    + int err = errno;
    + DPRINT(Debug,10,(&Debug,
    + "open_last_read_cache: fdopen %d: %s: %s\n",
    + file_fd,filename,strerror(err)));
    +
    + if (file_fd_locked) { /* Just ignore error --
    + close() should release this anyway
    + */
    + filelock_fd(file_fd,FLOCKING_release,locking_flags,
    + filename,FLOCKING_non_block,NULL);
    + }
    +
    + close(file_fd); /* Result ignored */
    +
    + if (errno_res)
    + *errno_res = err;
    +
    + return NULL;
    + }
    +
    +
    + ret = safe_zero_alloc(sizeof (*ret));
    +
    + ret->magic = LAST_READ_OPEN_magic;
    + ret->file_fd = file_fd;
    + ret->fh = f;
    + ret->filename = filename; /* shared */
    + ret->locking_flags = locking_flags;
    + ret->is_update = is_update;
    +
    + #define IFSET(var,flag) (var ? flag : 0)
    +
    + ret->lread_open_flags =
    + IFSET(file_fd_locked,LREAD_OPEN_FILE_FD_LOCKED);
    +
    + DPRINT(Debug,10,(&Debug,
    + "open_last_read_cache: %s open%s\n",
    + filename,
    + file_fd_locked ? ", file_fd locked" : ""));
    +
    + return ret;
    + }
    +
    + static void close_last_read_open P_((struct last_read_open *ptr));
    +
    + /* Closes ptr->fh if not update */
    + static int read_last_read_cache P_((struct last_read_open *ptr,
    + struct file_changes * cache_file,
    + struct sortlist ** cache_entries,
    + int * errors,
    + const struct last_read_entry_def * entry_default));
    + static int read_last_read_cache(ptr,cache_file,cache_entries,errors,entry_default)
    + struct last_read_open *ptr;
    + struct file_changes * cache_file;
    + struct sortlist ** cache_entries;
    + int * errors;
    + const struct last_read_entry_def * entry_default;
    + {
    + int ret = 0;
    + int suggest_rewrite = 0;
    +
    +
    + if (LAST_READ_OPEN_magic != ptr->magic)
    + panic("MBX PANIC",__FILE__,__LINE__,
    + "read_last_read_cache",
    + "Bad magic number (last_read_open)",0);
    +
    + if (! ptr->fh) {
    + ret = 0;
    + goto fail;
    + }
    +
    + rewind(ptr->fh);
    +
    + ret = parse_last_read_cache(ptr->filename,
    + ptr->fh,
    + cache_file,
    + cache_entries,
    + errors,
    + entry_default,
    + &suggest_rewrite);
    +
    + if (ptr -> is_update < last_read_check ||
    + (last_read_check == ptr -> is_update &&
    + ! suggest_rewrite)) {
    + close_last_read_open(ptr);
    + }
    +
    + if (suggest_rewrite)
    + setit(ptr->lread_open_flags,
    + LREAD_OPEN_SUGGEST_REWRITE);
    +
    + fail:
    +
    + DPRINT(Debug,14,(&Debug,
    + "read_last_read_cache=%d; filename=%s\n",
    + ret,ptr->filename));
    +
    +
    + return ret;
    +
    + }
    +
    + static void close_last_read_open(ptr)
    + struct last_read_open *ptr;
    + {
    + if ( LAST_READ_OPEN_magic != ptr->magic)
    + panic("MBX PANIC",__FILE__,__LINE__,
    + "close_last_read_entry",

    [continued in next message]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)