void exchange_katcl(struct katcl_line *l, int fd) { sane_line_katcl(l); /* WARNING: exchanging fds forces discarding of pending io */ if(l->l_fd >= 0){ close(l->l_fd); } l->l_fd = fd; if(l->l_ready){ destroy_parse_katcl(l->l_ready); l->l_ready = NULL; } if(l->l_next){ l->l_next = reuse_parse_katcl(l->l_next); } if(l->l_stage){ destroy_parse_katcl(l->l_stage); l->l_stage = NULL; } l->l_pending = 0; l->l_arg = 0; l->l_offset = 0; clear_queue_katcl(l->l_queue); l->l_error = 0; l->l_sendable = 1; }
int monitor_event_variable_katcp(struct katcp_dispatch *d, struct katcp_vrbl *vx, struct katcp_flat *fx) { struct katcp_subscribe *sub; struct katcl_parse *px; px = make_sensor_katcp(d, NULL, vx, KATCP_SENSOR_STATUS_INFORM); if(px == NULL){ return -1; } sub = locate_subscribe_katcp(d, vx, fx); if(sub == NULL){ sub = attach_variable_katcp(d, vx, fx); if(sub == NULL){ destroy_parse_katcl(px); return -1; } } sub->s_strategy = KATCP_STRATEGY_EVENT; append_parse_katcp(d, px); destroy_parse_katcl(px); return 0; }
struct katcl_parse *make_sensor_katcp(struct katcp_dispatch *d, char *name, struct katcp_vrbl *vx, char *prefix) { struct katcl_parse *px; #ifdef DEBUG fprintf(stderr, "sensor: triggering sensor change logic on sensor %s\n", name); #endif px = create_referenced_parse_katcl(); if(px == NULL){ return NULL; } if(add_string_parse_katcl(px, KATCP_FLAG_FIRST | KATCP_FLAG_STRING, prefix) < 0){ destroy_parse_katcl(px); return NULL; } if(add_partial_sensor_katcp(d, px, name, KATCP_FLAG_LAST, vx) < 0){ destroy_parse_katcl(px); return NULL; } return px; }
int change_sensor_katcp(struct katcp_dispatch *d, void *state, char *name, struct katcp_vrbl *vx) { struct katcp_wit *w; struct katcl_parse *px; w = state; sane_wit(w); #ifdef DEBUG fprintf(stderr, "sensor: triggering sensor change logic on sensor %s\n", name); #endif if(vx->v_flags & KATCP_VRF_HID){ return 0; } px = make_sensor_katcp(d, name, vx, KATCP_SENSOR_STATUS_INFORM); if(px == NULL){ return -1; } broadcast_subscribe_katcp(d, w, px); destroy_parse_katcl(px); return 0; }
void void_destroy_parse_katcl(void *v) { struct katcl_parse *px; px = v; destroy_parse_katcl(px); }
void destroy_katcl(struct katcl_line *l, int end) { if(l == NULL){ return; } /* in */ if(l->l_ready){ destroy_parse_katcl(l->l_ready); l->l_ready = NULL; } if(l->l_next){ destroy_parse_katcl(l->l_next); l->l_next = NULL; } /* out */ if(l->l_stage){ destroy_parse_katcl(l->l_stage); l->l_stage = NULL; } l->l_pending = 0; l->l_arg = 0; l->l_offset = 0; if(l->l_queue){ destroy_queue_katcl(l->l_queue); l->l_queue = NULL; } if(end){ if(l->l_fd >= 0){ close(l->l_fd); l->l_fd = (-1); } } l->l_error = EINVAL; l->l_sendable = 0; free(l); }
static int after_append_katcl(struct katcl_line *l, int flags, int result) { if(result < 0){ /* things went wrong, throw away the entire line */ #ifdef DEBUG fprintf(stderr, "append failed, discarding stage\n"); #endif if(l->l_stage){ destroy_parse_katcl(l->l_stage); l->l_stage = NULL; } return result; } if(!(flags & KATCP_FLAG_LAST)){ #if DEBUG > 1 fprintf(stderr, "after append: flag not last, not doing anything\n"); #endif return result; } /* probably redundant, before_append_katcl needs to check for this too */ if(l->l_stage == NULL){ #ifdef DEBUG fprintf(stderr, "after append: no stage variable\n"); #endif return -1; } add_tail_queue_katcl(l->l_queue, l->l_stage); destroy_parse_katcl(l->l_stage); l->l_stage = NULL; #ifdef KATCP_FLUSH_THRESHOLD if(size_queue_katcl(l->l_queue) > KATCP_FLUSH_THRESHOLD){ /* WARNING: ignores return code */ write_katcl(l); } #endif return result; }
int perform_sensor_update_katcp(struct katcp_dispatch *d, void *data) { struct katcp_shared *s; unsigned int i, j, count; struct katcp_flat *fx; struct katcp_group *gx; struct katcl_parse *px; s = d->d_shared; if(s == NULL){ return -1; } if(s->s_changes <= 0){ log_message_katcp(d, KATCP_LEVEL_WARN, NULL, "logic problem: scheduled device update, but nothing requires updating"); return -1; } px = create_referenced_parse_katcl(); if(px == NULL){ return -1; } add_string_parse_katcl(px, KATCP_FLAG_FIRST | KATCP_FLAG_STRING, KATCP_DEVICE_CHANGED_INFORM); add_string_parse_katcl(px, KATCP_FLAG_LAST | KATCP_FLAG_STRING, "sensor-list"); count = 0; for(j = 0; j < s->s_members; j++){ gx = s->s_groups[j]; for(i = 0; i < gx->g_count; i++){ fx = is_ready_flat_katcp(d, gx->g_flats[i]); if(fx){ if((fx->f_stale & KATCP_STALE_MASK_SENSOR) == KATCP_STALE_SENSOR_STALE){ fx->f_stale = KATCP_STALE_SENSOR_NAIVE; if((fx->f_flags & KATCP_FLAT_TOCLIENT) && (fx->f_flags & KATCP_FLAT_SEESKATCP)){ /* TODO: shouldn't we use the fancy queue infrastructure ? */ append_parse_katcl(fx->f_line, px); count++; } } } } } log_message_katcp(d, KATCP_LEVEL_DEBUG, NULL, "notified %u clients of %u sensor %s", count, s->s_changes, (s->s_changes == 1) ? "change" : "changes"); destroy_parse_katcl(px); s->s_changes = 0; return 0; }
void clear_katcl(struct katcl_line *l) /* discard a full line */ { if(l->l_ready == NULL){ #ifdef DEBUG fprintf(stderr, "unusual: clearing something which is empty\n"); #endif return; } destroy_parse_katcl(l->l_ready); l->l_ready = NULL; }
void destroy_remote(struct remote *rx) { unsigned int i; if(rx == NULL){ return; } if(rx->r_name){ free(rx->r_name); rx->r_name = NULL; } if(rx->r_line){ destroy_katcl(rx->r_line, 1); rx->r_line = NULL; } rx->r_index = 0; rx->r_state = RX_BAD; rx->r_match = NULL; if(rx->r_vector){ for(i = 0; i < rx->r_count; i++){ if(rx->r_vector[i]){ destroy_parse_katcl(rx->r_vector[i]); rx->r_vector[i] = NULL; } } free(rx->r_vector); rx->r_vector = NULL; } rx->r_count = 0; free(rx); }
int main(int argc, char **argv) { struct set *ss; struct remote *rx; struct katcl_parse *px; struct katcl_line *k; struct timeval delta, start, stop; fd_set fsr, fsw; char *app, *parm, *cmd, *copy, *ptr, *servers, *extra, *label; int i, j, c, fd, mfd, count; int verbose, result, status, info, timeout, flags, show, munge, once; int xmit, code; unsigned int len; servers = getenv("KATCP_SERVER"); if(servers == NULL){ servers = "localhost:7147"; } once = 1; munge = 0; info = 1; verbose = 1; i = j = 1; app = argv[0]; timeout = 0; k = NULL; show = 1; parm = NULL; extra = NULL; label = KCPPAR_NAME; count = 0; px = NULL; k = create_katcl(STDOUT_FILENO); if(k == NULL){ fprintf(stderr, "%s: unable to create katcp message logic\n", app); return 4; } ss = create_set(); if(ss == NULL){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to set up command set data structures"); return 4; } xmit = (-1); while (i < argc) { if (argv[i][0] == '-') { c = argv[i][j]; switch (c) { case 'h' : usage(app); return 0; case 'i' : info = 1 - info; j++; break; case 'm' : munge = 1; j++; break; case 'n' : show = 0; j++; break; case 'q' : verbose = 0; j++; break; case 'x' : xmit = 0; j++; break; case 'l' : case 's' : case 't' : j++; if (argv[i][j] == '\0') { j = 0; i++; } if (i >= argc) { sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "argument needs a parameter"); return 2; } switch(c){ case 'l' : label = argv[i] + j; break; case 's' : servers = argv[i] + j; break; case 't' : timeout = atoi(argv[i] + j); break; } i++; j = 1; break; case 'v' : verbose++; j++; break; case '-' : j++; break; case '\0': j = 1; i++; break; default: sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unknown option -%c", argv[i][j]); return 2; } } else { copy = NULL; if(xmit < 0){ /* WARNING: this could make error detection worse */ xmit = 0; } if(xmit == 0){ px = create_referenced_parse_katcl(); if(px == NULL){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to create parse instance for <%s ...>", argv[i]); return 4; } switch(argv[i][0]){ case KATCP_REQUEST : case KATCP_REPLY : case KATCP_INFORM : ptr = argv[i]; break; default : copy = malloc(strlen(argv[i]) + 1); if(copy == NULL){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to allocate temporary storage for %s", argv[i]); return 4; } copy[0] = KATCP_REQUEST; strcpy(copy + 1, argv[i]); ptr = copy; break; } flags = KATCP_FLAG_FIRST; } else { ptr = argv[i]; flags = 0; } i++; j = 1; if((i >= argc) || (argv[i][0] == '-')){ flags |= KATCP_FLAG_LAST; } if(add_string_parse_katcl(px, flags, ptr) < 0){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to add parameter %s", ptr); return 4; } if(flags & KATCP_FLAG_LAST){ #ifdef DEBUG fprintf(stderr, "par: loading command for servers %s\n", servers); #endif if(load_parse_set(ss, servers, px) < 0){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to load command into server set %s", servers); return 4; } destroy_parse_katcl(px); px = NULL; } if(copy){ free(copy); copy = NULL; } xmit++; } } if(timeout == 0){ timeout = 5000 * ss->s_count; } gettimeofday(&start, NULL); delta.tv_sec = timeout / 1000; delta.tv_usec = (timeout % 1000) * 1000; add_time_katcp(&stop, &start, &delta); if(activate_remotes(ss, k) < 0){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to initiate connections to remote servers"); return 3; } for(ss->s_finished = 0; ss->s_finished < ss->s_count;){ mfd = 0; FD_ZERO(&fsr); FD_ZERO(&fsw); if(k){ if(flushing_katcl(k)){ mfd = fileno_katcl(k); FD_SET(mfd, &fsw); } } for(i = 0; i < ss->s_count; i++){ rx = ss->s_vector[i]; if(rx->r_line){ fd = fileno_katcl(rx->r_line); if(fd > mfd){ mfd = fd; } } else { fd = (-1); /* WARNING: live dangerously */ } switch(rx->r_state){ case RX_SETUP : FD_SET(fd, &fsw); break; case RX_UP : if(flushing_katcl(rx->r_line)){ /* only write data if we have some */ FD_SET(fd, &fsw); } FD_SET(fd, &fsr); break; /* case RX_OK : */ /* case RX_FAIL : */ /* case RX_BAD : */ default : break; } } gettimeofday(&start, NULL); sub_time_katcp(&delta, &stop, &start); result = select(mfd + 1, &fsr, &fsw, NULL, &delta); switch(result){ case -1 : switch(errno){ case EAGAIN : case EINTR : continue; /* WARNING */ default : sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "select failed: %s", strerror(errno)); return 4; } break; case 0 : sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "requests timed out after %dms", timeout); /* could terminate cleanly here, but ... */ return 3; } if(k){ fd = fileno_katcl(k); if(FD_ISSET(fd, &fsw)){ write_katcl(k); /* WARNING: ignores write failures - unable to do much about it */ } } for(i = 0; i < ss->s_count; i++){ rx = ss->s_vector[i]; if(rx->r_line){ fd = fileno_katcl(rx->r_line); } else { fd = (-1); /* WARNING: live dangerously, will cause select to core dump if logic is incorrect */ } switch(rx->r_state){ case RX_SETUP : if(FD_ISSET(fd, &fsw)){ len = sizeof(int); result = getsockopt(fd, SOL_SOCKET, SO_ERROR, &code, &len); if(result == 0){ switch(code){ case 0 : if(verbose){ log_message_katcl(k, KATCP_LEVEL_DEBUG, label, "async connect to %s succeeded", rx->r_name); } if(next_request(rx) < 0){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "failed to load request for destination %s", rx->r_name); update_state(ss, rx, RX_BAD); } else { update_state(ss, rx, RX_UP); } break; case EINPROGRESS : log_message_katcl(k, KATCP_LEVEL_WARN, label, "saw an in progress despite write set being ready on job %s", rx->r_name); break; default : log_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to connect to %s: %s", rx->r_name, strerror(code)); update_state(ss, rx, RX_BAD); break; } } } break; case RX_UP : if(FD_ISSET(fd, &fsw)){ /* flushing things */ result = write_katcl(rx->r_line); if(result < 0){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to write to %s: %s", rx->r_name, strerror(error_katcl(rx->r_line))); update_state(ss, rx, RX_BAD); } } if(FD_ISSET(fd, &fsr)){ /* get things */ result = read_katcl(rx->r_line); if(result){ if(result < 0){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "read from %s failed: %s", rx->r_name, strerror(error_katcl(rx->r_line))); } else { log_message_katcl(k, KATCP_LEVEL_WARN, label, "%s disconnected", rx->r_name); } } } while(have_katcl(rx->r_line) > 0){ /* compute */ cmd = arg_string_katcl(rx->r_line, 0); if(cmd){ #ifdef DEBUG fprintf(stderr, "reading message <%s ...>\n", cmd); #endif switch(cmd[0]){ case KATCP_INFORM : if(info){ if(show == 0){ if(!strcmp(KATCP_VERSION_CONNECT_INFORM, cmd)){ break; } if(!strcmp(KATCP_VERSION_INFORM, cmd)){ break; } if(!strcmp(KATCP_BUILD_STATE_INFORM, cmd)){ break; } } relay_katcl(rx->r_line, k); } break; case KATCP_REPLY : switch(cmd[1]){ case ' ' : case '\n' : case '\r' : case '\t' : case '\\' : case '\0' : log_message_katcl(k, KATCP_LEVEL_ERROR, label, "unreasonable response message from %s", rx->r_name); update_state(ss, rx, RX_BAD); break; default : ptr = cmd + 1; if(strcmp(ptr, rx->r_match)){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "downstream %s returned response %s which was never requested", rx->r_name, ptr); update_state(ss, rx, RX_BAD); } else { parm = arg_string_katcl(rx->r_line, 1); if(parm){ if(strcmp(parm, KATCP_OK) == 0){ count++; if(munge){ log_message_katcl(k, KATCP_LEVEL_INFO, label, "%s %s ok", rx->r_name, ptr); } if(verbose > 1){ log_message_katcl(k, KATCP_LEVEL_TRACE, label, "request %s to %s returned ok", ptr, rx->r_name); } result = next_request(rx); if(result){ if(result < 0){ sync_message_katcl(k, KATCP_LEVEL_ERROR, label, "unable to queue request %s to %s", ptr, rx->r_name); update_state(ss, rx, RX_BAD); } else { update_state(ss, rx, RX_OK); } } } else { if(munge){ extra = arg_string_katcl(rx->r_line, 2); log_message_katcl(k, KATCP_LEVEL_ERROR, label, "%s %s %s (%s)", rx->r_name, ptr, parm, extra ? extra : "no extra information"); } if(verbose > 0){ log_message_katcl(k, KATCP_LEVEL_ERROR, label, "downstream %s unable to process %s with status %s (%s)", rx->r_name, cmd, parm, extra ? extra : "no extra information"); } update_state(ss, rx, RX_FAIL); } } else { log_message_katcl(k, KATCP_LEVEL_ERROR, label, "response %s without status from %s", cmd, rx->r_name); update_state(ss, rx, RX_FAIL); } } break; } break; case KATCP_REQUEST : log_message_katcl(k, KATCP_LEVEL_WARN, label, "encountered unanswerable request %s", cmd); update_state(ss, rx, RX_BAD); break; default : if(once){ log_message_katcl(k, KATCP_LEVEL_WARN, label, "read malformed message %s from %s", cmd, rx->r_name); once = 1; } break; } } } break; /* case RX_OK : */ /* case RX_FAIL : */ /* case RX_BAD : */ default : break; } } } status = ss->s_status; destroy_set(ss); if(verbose){ if(status > 0){ log_message_katcl(k, KATCP_LEVEL_WARN, label, "command sequence failed after operation %d", count); } else { if(count > 0){ log_message_katcl(k, KATCP_LEVEL_INFO, label, "%d operations ok", count); } else { log_message_katcl(k, KATCP_LEVEL_INFO, label, "did nothing successfully"); } } } /* flush, allows us to get away with deferring writes to stdout */ while(write_katcl(k) == 0); destroy_katcl(k, 0); return status; }
int write_katcl(struct katcl_line *l) { int wr; int state; unsigned int space, want, can, actual; struct katcl_parse *p; struct katcl_larg *la; #define TMP_MARGIN 32 p = NULL; for(state = WRITE_STATE_FILL; state != WRITE_STATE_DONE; ){ #if DEBUG > 1 fprintf(stderr, "write: state=%d, parse=%p, arg=%u, offset=%u, pending=%u\n", state, p, l->l_arg, l->l_offset, l->l_pending); #endif switch(state){ case WRITE_STATE_FILL : p = get_head_queue_katcl(l->l_queue); if(p == NULL){ state = (l->l_pending > 0) ? WRITE_STATE_SEND : WRITE_STATE_DONE; continue; /* WARNING */ } #ifdef KATCP_CONSISTENCY_CHECKS if(p->p_magic != KATCL_PARSE_MAGIC){ fprintf(stderr, "write: bad magic returned from get_head (%x, expected %x)\n", p->p_magic, KATCL_PARSE_MAGIC); abort(); } #endif if((l->l_pending + TMP_MARGIN) > KATCL_IO_SIZE){ state = WRITE_STATE_SEND; /* drain if no margin */ continue; /* WARNING */ } #ifdef KATCP_CONSISTENCY_CHECKS if(l->l_arg >= p->p_got){ fprintf(stderr, "write: logic problem: arg=%u >= got=%u\n", l->l_arg, p->p_got); abort(); } #endif la = &(p->p_args[l->l_arg]); #ifdef KATCP_CONSISTENCY_CHECKS if((la->a_begin + l->l_offset) > la->a_end){ fprintf(stderr, "write: logic problem: offset=%u extends beyond argument %u (%u-%u)\n", l->l_offset, l->l_arg, la->a_begin, la->a_end); abort(); } #endif if((la->a_begin + l->l_offset) >= la->a_end){ /* done ? */ if(l->l_offset == 0){ /* special case - null arg */ #ifdef KATCP_CONSISTENCY_CHECKS if(l->l_arg == 0){ fprintf(stderr, "write: problem - arg0 is null\n"); abort(); } #endif strcpy(l->l_buffer + l->l_pending, "\\@"); l->l_pending += 2; } if(la->a_escape <= 1){ /* mark things which were thought to need escaping, but did not appropriately */ la->a_escape = 0; } l->l_arg++; l->l_offset = 0; state = WRITE_STATE_NEXT; continue; } want = la->a_end - (la->a_begin + l->l_offset); space = KATCL_IO_SIZE - (l->l_pending + 1); #if DEBUG>1 fprintf(stderr, "write: arg[%u] has %u more, space is %u\n", l->l_arg, want, space); #endif if(la->a_escape){ can = ((space / 2) >= want) ? want : space / 2; actual = escape_copy_katcl(l->l_buffer + l->l_pending, p->p_buffer + la->a_begin + l->l_offset, can); if(actual > can){ la->a_escape = 2; /* record that we needed to escape */ } l->l_pending += actual; } else { can = (space >= want) ? want : space; memcpy(l->l_buffer + l->l_pending, p->p_buffer + la->a_begin + l->l_offset, can); l->l_pending += can; } l->l_offset += can; break; case WRITE_STATE_NEXT : /* move onto next */ /* gets a proposed position, updates to next parse or end if needed, adding separators and tags */ p = get_head_queue_katcl(l->l_queue); if(p == NULL){ l->l_offset = 0; l->l_arg = 0; state = WRITE_STATE_SEND; continue; } #ifdef KATCP_CONSISTENCY_CHECKS if(p->p_magic != KATCL_PARSE_MAGIC){ fprintf(stderr, "write: bad magic returned from get_head (%x, expected %x)\n", p->p_magic, KATCL_PARSE_MAGIC); abort(); } #endif if(l->l_pending >= KATCL_IO_SIZE){ fprintf(stderr, "write: logic problem: pending=%u, no space left\n", l->l_pending); } if((p->p_tag >= 0) && (l->l_arg == 1)){ /* !#$ : TODO: enter tag printing state */ } if(l->l_arg < p->p_got){ /* more args */ state = WRITE_STATE_FILL; l->l_buffer[l->l_pending++] = ' '; continue; } l->l_arg = 0; l->l_buffer[l->l_pending++] = '\n'; #if DEBUG > 1 fprintf(stderr, "write: wrote out parse %p (refs %d)\n", p, p->p_refs); #endif p = remove_head_queue_katcl(l->l_queue); destroy_parse_katcl(p); state = p ? WRITE_STATE_FILL : WRITE_STATE_SEND; break; case WRITE_STATE_SEND : /* do io */ p = get_head_queue_katcl(l->l_queue); if(l->l_pending <= 0){ /* nothing more to write ? */ state = p ? WRITE_STATE_FILL : WRITE_STATE_DONE; continue; /* WARNING: added later, attempt to remove write(,,0) cases */ } if(l->l_sendable){ #ifdef MSG_NOSIGNAL wr = send(l->l_fd, l->l_buffer, l->l_pending, MSG_DONTWAIT | MSG_NOSIGNAL); #else wr = send(l->l_fd, l->l_buffer, l->l_pending, MSG_DONTWAIT); #endif } else { wr = write(l->l_fd, l->l_buffer, l->l_pending); } if(wr < 0){ switch(errno){ case EAGAIN : case EINTR : return 0; /* returns zero if still more to do */ case ENOTSOCK : if(l->l_sendable > 0){ l->l_sendable = 0; /* try again, this time with write() not send() */ continue; /* WARNING, restart for(); */ } /* WARNING: drop through */ default : l->l_error = errno; return -1; } } if(wr > 0){ if(wr < l->l_pending){ /* a tad expensive, but we expect this to be rare */ memmove(l->l_buffer, l->l_buffer + wr, l->l_pending - wr); l->l_pending -= wr; } else { l->l_pending = 0; } } break; } } /* done everything */ return 1; #undef TMP_MARGIN }
int main() { struct katcl_line *l; struct katcl_parse *p; int count, seed, i, k, fds[2], result, al, bl; char alpha[MAX_ARG_LEN], beta[MAX_ARG_LEN]; pid_t pid; seed = getpid(); printf("line test: seed is %d\n", seed); srand(seed); if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0){ fprintf(stderr, "line: unable to create socketpair\n"); return 1; } pid = fork(); if(pid < 0){ fprintf(stderr, "line: unable to fork\n"); return 1; } if(pid == 0){ close(fds[0]); echobuffer(fds[1]); return 0; } close(fds[1]); l = create_katcl(fds[0]); if(l == NULL){ fprintf(stderr, "main: unable to create katcl\n"); return 1; } for(i = 0; i < TEST_RUNS; i++){ #ifdef DEBUG fprintf(stderr, "line test: iteration %d\n", i); #endif p = create_referenced_parse_katcl(); if(p == NULL){ fprintf(stderr, "unable to create parse instance %d\n", i); return 1; } #ifdef DEBUG fprintf(stderr, "test: ref before submission %d\n", p->p_refs); #endif fill_random_test(p); dump_parse_katcl(p, "random", stderr); if(append_parse_katcl(l, p) < 0){ fprintf(stderr, "unable to add parse %d\n", i); return 1; } #ifdef DEBUG fprintf(stderr, "test: ref after submission %d\n", p->p_refs); #endif while((result = write_katcl(l)) == 0); #ifdef DEBUG fprintf(stderr, "test: ref after write %d\n", p->p_refs); #endif if(result < 0){ fprintf(stderr, "unable to write data\n"); return 1; } do{ result = read_katcl(l); if(result){ fprintf(stderr, "read result is %d\n", result); return 1; } } while(have_katcl(l) == 0); count = arg_count_katcl(l); for(k = 0; k < count; k++){ al = arg_buffer_katcl(l, k, alpha, MAX_ARG_LEN); bl = get_buffer_parse_katcl(p, k, beta, MAX_ARG_LEN); if((bl < 0) || (al < 0)){ fprintf(stderr, "al=%d, bl=%d\n", al, bl); return 1; } if(al != bl){ fprintf(stderr, "al=%d != bl=%d\n", al, bl); return 1; } if(memcmp(alpha, beta, al)){ fprintf(stderr, "mismatch: round=%d, arg=%d\n", i, k); return 1; } } fprintf(stderr, "parsed a line with %d words\n", count); destroy_parse_katcl(p); } destroy_katcl(l, 1); printf("line test: ok\n"); return 0; }