int slice_foreach_value(slice_t *slice, int (*item_handler)(void *item, size_t idx, void *user), void *user) { linked_list_t *list = slice->list; MUTEX_LOCK(list->lock); size_t idx = 0; list_entry_t *e = pick_entry(list, slice->offset); while(e && idx < slice->length) { int rc = item_handler(e->value, idx++, user); if (rc == 0) { break; } else if (rc == -1 || rc == -2) { list_entry_t *d = e; e = e->next; if (list->head == list->tail && list->tail == d) { list->head = list->tail = NULL; } else if (d == list->head) { list->head = d->next; list->head->prev = NULL; } else if (d == list->tail) { list->tail = d->prev; list->tail->next = NULL; } else { e->prev = d->prev; e->prev->next = e; } d->list = NULL; if (list->cur == d) list->cur = NULL; list->length--; slice->length--; // the callback got the value and will take care of releasing it destroy_entry(d); if (rc == -2) // -2 means : remove and stop the iteration break; // -1 instead means that we still want to remove the item // but we also want to go ahead with the iteration } else { e = e->next; } } MUTEX_UNLOCK(list->lock); return idx; }
/* Read configuration file. Exit if it is unreadable or invalid. * The configuration format consists of Bourne shell variable assignments * and comments, with no variable references or escaped line breaks * allowed. */ static void read_config(const char * path, void (*item_handler)(const char *, const char *)) { FILE * file; unsigned line_no = 0; char line_buf[1000]; /* this is just going to have to be long enough */ if ((file = fopen(path, "r")) == NULL) { /* Configuration files are optional so this is only an error if * the file exists yet is unreadable. */ if (errno == ENOENT) return; perror("ERROR: fopen"); exit(1); } while (fgets(line_buf, sizeof(line_buf), file)) { const char * name, * value; char * p; int ch; bool valid = true; ++line_no; /* Find first non-space. */ p = line_buf; while ((ch = (unsigned char)*p) && isspace(ch)) ++p; if (ch == 0 || ch == '#') { /* This is a blank or comment line. */ continue; } else if (isalpha(ch) || ch == '_') { /* Find end of name. */ name = p++; while ((ch = (unsigned char)*p) && (isalnum(ch) || ch == '_')) ++p; *p = 0; /* ensure name is terminated */ if (ch != '=') { valid = false; } else { char * out; ++p; value = out = p; while ((ch = (unsigned char)*p) && !isspace(ch)) { int quote = (ch == '\'' || ch == '"') ? ch : 0; if (quote) ++p; while ((ch = (unsigned char)*p) && !(quote ? ch == quote : (isspace(ch) || ch == '\'' || ch == '"'))) { ++p; if (quote != '\'') { if (ch == '$') { /* We're not going to do $-expansion. */ valid = false; } else if (ch == '\\') { /* Check whether it's a valid escape. */ ch = (unsigned char)*p; switch (ch) { case '$': case '\'': case '\"': case '\\': ++p; break; case ' ': if (quote) valid = false; else ++p; break; default: valid = false; break; } } } *out++ = ch; } if (quote && ch) ++p; } while ((ch = (unsigned char)*p) && isspace(ch) && ch != '\n') ++p; if (ch != '\n') valid = false; /* new-line was quoted or missing */ *out = 0; /* terminate value */ } } else { valid = false; } if (valid) { item_handler(name, value); } else { /* XXX This could be more informative! */ fprintf(stderr, "ERROR: syntax error at %s:%d\n", path, line_no); exit(2); } } fclose(file); }