/* * This function simulate summing up three positive numbers * using add function. * * The code deals with memory failure and invalid arguments. * The code responds to KILL signal and kill sub-FUNK accordingly. * The code calls sub-FUNK and YIELD. */ Continuation * sum(Continuation * co, int a, int b, int c, int * ret) { VAR_BEGIN int sum; Continuation* sub; VAR_END if (!this || a < 0 || b < 0 || c < 0) { *ret = -1; EXIT(); } if (KILL_SIGNALLED()) { KILL_FUNC(this->sub, add, 0, 0, 0); EXIT(); } while (CALL_FUNK(this->sub, add, a, b, &this->sum)) YIELD(); if (this->sum == -1) // mem fail { *ret = -1; EXIT(); } while (CALL_FUNK(this->sub, add, this->sum, c, ret)) YIELD(); EXIT(); }
/* ===================================================================== * Wait for the turn of the thread denoted by 'tid' to do its checking * ===================================================================== */ void WaitForThisThreadToBeActive(THREADID tid) { // Waits for the turn of this thread while (VecThreadIds[ActiveThreadIndex] != tid) { PIN_MutexUnlock(&MtxActiveThread); YIELD(); PIN_MutexLock(&MtxActiveThread); } // At this point it's the turn of the current thread to do the checking // the previous thread release the mutex MtxActiveThread. // We still need to wait (for at most 10 seconds) for the previous thread // to stop at the debugger time_t startWaitingTime = time(NULL); if (ActiveThreadIndex > 0) { THREADID prevTid = VecThreadIds[ActiveThreadIndex - 1]; while (!PIN_IsThreadStoppedInDebugger(prevTid)) { ASSERT((time(NULL) - startWaitingTime) < 10, "Timeout waiting for thread " + hexstr(prevTid) + " to stop in debugger"); YIELD(); } } }
int main() { int round; int t; GET_NTDLL(NtTerminateProcess, (IN HANDLE ProcessHandle OPTIONAL, IN NTSTATUS ExitStatus)); INIT(); for (round = 0; round < ROUNDS; round++) { #ifdef VERBOSE print("round %d\n", round); #endif if (round > 0) { /* clean up first */ NtTerminateProcess(0 /* everyone but me */, 666); #ifdef VERBOSE print("all alone again %d\n", round); #endif VERBOSE } global_started = 0; global_finished = 0; for (t = 0; t < TOTAL_THREADS; t++) { thread[t] = (HANDLE)create_thread(executor); if (thread[t] == NULL) print("GLE: %d\n", GetLastError()); assert(thread[t] != NULL); } #ifdef VERBOSE print("started %d threads\n", TOTAL_THREADS); #else print("started some threads\n"); #endif /* wait for some to start */ while (global_started < WAIT_TO_START) YIELD(); /* wait for some work to get done */ while (global_finished < WAIT_TO_FINISH) YIELD(); #ifdef VERBOSE print("some %d work, done %d\n", global_started, global_finished); #endif } print("done\n"); return 0; }
int main(int argc, char **argv) { HANDLE lib; HANDLE event; ULONG_PTR hThread; int reloaded = 0; USE_USER32(); #ifdef UNIX /* though win32/ */ intercept_signal(SIGSEGV, signal_handler); #else /* note that can't normally if we have a debugger attached this * will not get this executed */ SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)our_top_handler); #endif event = CreateEvent(NULL, TRUE, FALSE, NULL); hThread = _beginthreadex(NULL, 0, run_func, event, 0, NULL); /* need to run as long as necessary to hit the required faults */ while (transitions < NUM_TRANSITIONS) { lib = LoadLibrary("win32.reload-race.dll.dll"); if (lib == NULL) { print("error loading library\n"); break; } else { reloaded++; #if VERBOSE print("reloaded %d times %d\n", reloaded); #endif import1 = (funptr)GetProcAddress(lib, (LPCSTR) "import_me1"); import2 = (funptr)GetProcAddress(lib, (LPCSTR) "import_me2"); /* may want to explicitly sleep here but that may be slower */ if (reloaded % 2 == 0) YIELD(); FreeLibrary(lib); if (reloaded % 3 == 0) YIELD(); } } SetEvent(event); WaitForSingleObject((HANDLE)hThread, INFINITE); print("main loop done\n"); check_mem_usage(); #if VERBOSE print("reloaded %d times %d\n", reloaded); #endif return 0; }
lint_iterator primes (){ YIELDS(lint); YIELD(2); lint n = 3; while( n < max_lint - 2 ){ YIELD(n); n += 2; while( n < max_lint - 2 && ! is_prime_cached(n) ){ n += 2; } } }
static int regmatch_sub_anytime(struct MatchingContext2 *c, Regex *regex, regexchar * pat2, char *str, char *end_p, int iter_limit, int firstp) { switch (c->label) { case 1: goto label1; case 2: goto label2; case 3: goto label3; } c->ctx = GC_malloc(sizeof(struct MatchingContext1)); c->ctx2 = GC_malloc(sizeof(struct MatchingContext2)); c->ctx->label = 0; c->regex = regex; c->n_any = 0; c->str = str; c->firstp = firstp; for (;;) { c->ctx->label = 0; while (regmatch_iter(c->ctx, c->regex->re, c->str, end_p, c->firstp)) { c->n_any = c->ctx->lastpos - c->str; if (c->n_any <= 0) continue; c->firstp = 0; if (RE_MODE(pat2) == RE_ENDMARK) { c->lastpos = c->str + c->n_any; YIELD(1, c, 1); } else if (regmatch(pat2, c->str + c->n_any, end_p, c->firstp, &c->lastpos) == 1) { YIELD(1, c, 2); } if (iter_limit == 1) continue; c->ctx2->label = 0; while (regmatch_sub_anytime(c->ctx2, regex, pat2, c->str + c->n_any, end_p, iter_limit - 1, c->firstp)) { c->lastpos = c->ctx2->lastpos; YIELD(1, c, 3); } } if (c->regex->alt_regex == NULL) break; c->regex = c->regex->alt_regex; } return 0; }
void CONTROLLER_MainLoop(void) { static int32_t ret; static int32_t adv; /* Executed Every Loop */ SERIAL_ProtocolBackground(&Serial); if ( SYSTEM_GetFlagLCD() == 1) { SYSTEM_ClrFlagLCD(); VIEW_Refresh(); } if( Serial.Packet_BufferNotEmpty == 1 ){ Serial.Packet_BufferNotEmpty = 0; return; } START(FLAG_10ms) TIMER_DriverCount(&TIMER_OneHour); ADC_DriverSoftStart(); YIELD(FLAG_10ms) KEYMATRIX_DriverScan(); TTC_Timer2Char(&TIMER_OneHour, &self_view); YIELD(FLAG_10ms) adv = ADC_DriverGetVoltage(); VTC_Voltage2Char(adv, &self_view); __SelfData(); YIELD(FLAG_10ms) SERIAL_ProtocolSend(&Serial, &packet_tx[0], 2); YIELD(FLAG_10ms) ret = SERIAL_ProtocolRecv(&Serial, packet, 2); YIELD(FLAG_10ms) if (ret < 0) { __VIEW_StateMachine(EVENT_RX_ERROR); } else { while (ret--) { __Recv2View(&(packet[ret]), &rx_view); } } YIELD(FLAG_10ms) VIEW_Render(VIEW_State, __VIEW_StateMachine(0)); END(FLAG_10ms) }
static void t_wait(void *a) { hal_set_current_thread_priority( THREAD_PRIO_HIGH ); char *name = a; while(!thread_stop_request) { thread_activity_counter++; if(TEST_CHATTY) printf("--- thread %s will wait 4 cond ---\n", name); hal_mutex_lock(&m); checkEnterMutex(); checkLeaveMutex(); hal_cond_wait(&c, &m); checkEnterMutex(); checkLeaveMutex(); hal_mutex_unlock(&m); if(TEST_CHATTY) printf("--- thread %s runs ---\n", name); //pressEnter("--- thread a runs ---\n"); YIELD(); } FINISH(); }
///Rectangle generator GridItemPtr Grid::rectangle_generator::operator()() { //Generator preamble BEGIN_RESTORE_STATE; RESTORE_STATE(state1); END_RESTORE_STATE; //Generator body BEGIN_GENERATOR; ix0 = limit( (int)floor(x0*grid->cellScale.x), 0, grid->numCols ); iy0 = limit( (int)floor(y0*grid->cellScale.y), 0, grid->numRows ); ix1 = limit( (int)ceil(x1*grid->cellScale.x), 0, grid->numCols ); iy1 = limit( (int)ceil(y1*grid->cellScale.y), 0, grid->numRows ); //note that local variable are not used, because we are inside pseudo-gnerator for (y=iy0; y<=iy1;++y){ for (x=ix0; x<=ix1;++x){ cur_cell = &(grid->cellRef(x,y)); i = cur_cell->items.begin(); while ( i!=cur_cell->items.end() ){ if ( in_rect((*i)->getPos(), x0, y0, x1, y1) ){ YIELD( *i, state1 ); } ++i; } } } END_GENERATOR; return GridItemPtr(); //dummy return, just to exit the generator }
int modem_response(char *str, size_t maxlen, int timeout) { char ch; size_t len=0; time_t start; start=time(NULL); while(1){ /* Abort with keystroke */ if(kbhit()) { getch(); return(1); } if(time(NULL)-start >= timeout) return(-1); if(len >= maxlen) return(-1); if(!comReadByte(com, &ch)) { YIELD(); continue; } if(ch<' ' && len==0) /* ignore prepended control chars */ continue; if(ch=='\r') { // while(comReadByte(com,&ch)); /* eat trailing ctrl chars (e.g. 'LF') */ break; } str[len++]=ch; } str[len]=0; return(0); }
JSBool DLLCALL js_CommonBranchCallback(JSContext *cx, js_branch_t* branch) { branch->counter++; /* Terminated? */ if(branch->auto_terminate && (branch->terminated!=NULL && *branch->terminated)) { JS_ReportError(cx,"Terminated"); branch->counter=0; return(JS_FALSE); } /* Infinite loop? */ if(branch->limit && branch->counter > branch->limit) { JS_ReportError(cx,"Infinite loop (%lu branches) detected",branch->counter); branch->counter=0; return(JS_FALSE); } /* Give up timeslices every once in a while */ if(branch->yield_interval && (branch->counter%branch->yield_interval)==0) YIELD(); /* Periodic Garbage Collection */ if(branch->gc_interval && (branch->counter%branch->gc_interval)==0) JS_MaybeGC(cx), branch->gc_attempts++; return(JS_TRUE); }
static errno_t threads_test() { hal_cond_init(&c, "threadTest"); hal_mutex_init(&m, "threadTest"); hal_sem_init(&s, "threadTest"); int i = 40; n_t_empty = i; while(i-- > 0) phantom_create_thread( t_empty, "Empty", 0 ); pressEnter("will create thread"); phantom_create_thread( thread1, "__T1__", 0 ); phantom_create_thread( thread1, "__T2__", 0 ); //phantom_create_thread( thread1, "__T3__" ); //phantom_create_thread( t_wait, "__TW__" ); int tid = hal_start_kernel_thread_arg( t_wait, "__TW__" ); i = 40; while(i-- > 0) { if(TEST_CHATTY) pressEnter("will yield"); YIELD(); if(TEST_CHATTY) printf("!! back in main\n"); } t_kill_thread( tid ); hal_sleep_msec( 30 ); thread_stop_request = 1; hal_sleep_msec( 10 ); thread_activity_counter = 0; hal_sleep_msec( 1000 ); if( thread_activity_counter ) { SHOW_ERROR0( 0, "Can't stop thread" ); return -1; } while(n_t_empty > 0) { SHOW_FLOW( 0, "wait for %d threads", n_t_empty ); hal_sleep_msec(500); } if(p._ah.refCount != 1) { SHOW_ERROR( 0, "p._ah.refCount = %d", p._ah.refCount ); test_fail_msg( -1, "refcount" ); } else SHOW_ERROR( 0, "p._ah.refCount = %d, SUCCESS", p._ah.refCount ); return 0; }
void uvc_return(){ uvc_ctx *ctx=uvc_self(); uvc_thread_env *env = uvc_get_env(); env->runing_task->status= UVC_STATUS_DIE; printf("task[%s] exit\n",uvc_ctx_get_name()); YIELD(env); //TODO 当前协程先出栈,然后resume到专门释放协程的协程。 }
void setBoardtoMove(Board inboard, Board outboard, int loc_apex, int block_type, DIR dir, int steps, BOOL settomemo, Node *prenode) { Node *node = NULL; Board board = NULL; DEBUG("IN"); if (settomemo) { board = (Board)malloc(BOARD_CELLS); memcpy(board, inboard, BOARD_CELLS); } else { board = outboard; } switch(block_type) { case SB: board[loc_apex] = NB; board[loc_apex + dir * steps] = block_type; break; case VG: board[loc_apex] = NB; board[loc_apex + DOWN] = NB; board[loc_apex + dir * steps] = block_type; board[loc_apex + dir * steps + DOWN] = block_type; break; case HG: board[loc_apex] = NB; board[loc_apex + RIGHT] = NB; board[loc_apex + dir * steps] = block_type; board[loc_apex + dir * steps + RIGHT] = block_type; break; case CC: board[loc_apex] = NB; board[loc_apex + RIGHT] = NB; board[loc_apex + DOWN] = NB; board[loc_apex + DOWN + RIGHT] = NB; board[loc_apex + dir * steps] = block_type; board[loc_apex + dir * steps + RIGHT] = block_type; board[loc_apex + dir * steps + DOWN] = block_type; board[loc_apex + dir * steps + DOWN + RIGHT] = block_type; break; default: ERROR("invalid block_type"); break; } if (settomemo && !isVisited(board)) { memcpy(outboard, board, BOARD_CELLS); node = ht->put(board); node->data->prev = prenode; enQue(que, node); #if FALSE YIELD("move to board"); printBoard(board); #endif } DEBUG("OUT"); }
iterator fibonacci(int stop){ YIELDS(int); int f[] = {0, 1}; int i; for(i=0; i<stop; i++){ YIELD(f[i%2]); f[i%2]=f[0]+f[1]; } }
void main(int argc, char *argv[]) { int skt; int err, quit=0; int DataBytes; InitLib(); err=NetStart(); if(err<0) { Print("Init Ethernet error.\n\r"); return; } GetIp(MyIp); Print("IP=%d.%d.%d.%d\r\n", MyIp[0], MyIp[1], MyIp[2], MyIp[3]); skt=Nopen("*", "UDP/IP", 10000, 0, S_NOCON | S_NOWA); if(skt<0) { Print("Cannot open a connection for UDP/IP!"); Nterm(); return; } Print("Socket=%d\r\n", skt); SOCKET_NOBLOCK(skt); while(!quit) { YIELD(); // must add this line in the while loop DataBytes=Nread(skt, InBuf, 1500); if(DataBytes>0) { Print("%s\n\r",InBuf); memcpy(OutBuf, InBuf, DataBytes); Nwrite(skt, OutBuf, DataBytes); } if(Kbhit()) { switch(Getch()) { case 'q': case 'Q': quit=1; break; } } } Nclose(skt); Nterm(); }
lint_iterator decompose (lint in_n){ YIELDS(lint); lint n = in_n; /* for p in primes do */ FOR(lint p, primes()){ if( p*p > n ){ BREAK; } else { while( n % p == 0 ){ YIELD(p); n = n / p; } } CONTINUE; } if( n > 1 ){ YIELD(n); } }
GridItemPtr Grid::items_generator::operator()() { BEGIN_RESTORE_STATE; RESTORE_STATE( state1 ); RESTORE_STATE( state2 ); END_RESTORE_STATE; BEGIN_GENERATOR; for( i=0; i<grid->numRows*grid->numCols; ++i){ curCell = &(grid->cells[i]); for( iItem = curCell->items.begin(); iItem != curCell->items.end(); ++iItem){ YIELD( *iItem, state1 ); } } curCell = &(grid->unallocated); for( iItem = curCell->items.begin(); iItem != curCell->items.end(); ++iItem){ YIELD( *iItem, state2 ); } END_GENERATOR; return GridItemPtr(); }
Continuation * sum(Continuation * co, int a, int b, int c, int * ret) { VAR_BEGIN int sum; Continuation* sub; VAR_END if (!this) { *ret = -1; EXIT(); } this->sub = 0; while(CALL_FUNK(this->sub, add, a, b, &this->sum)) YIELD(); if (this->sum == -1) { // mem fail *ret = -1; EXIT(); } while(CALL_FUNK(this->sub, add, this->sum, c, ret)) YIELD(); EXIT(); }
//Circular generator GridItemPtr Grid::circular_generator::operator()() { //TODO Possible optimization: instead of skipping the cells that are too far, // enumerate only cells that are near enough. This would require more // computations though. BEGIN_RESTORE_STATE; RESTORE_STATE( state1 ); END_RESTORE_STATE; BEGIN_GENERATOR; //first determine rectangle, where to search for ix0 = limit( (int)floor((center.x-r)*grid->cellScale.x), 0, grid->numCols ); iy0 = limit( (int)floor((center.y-r)*grid->cellScale.y), 0, grid->numRows ); ix1 = limit( (int)ceil((center.x+r)*grid->cellScale.x), 0, grid->numCols ); iy1 = limit( (int)ceil((center.y+r)*grid->cellScale.y), 0, grid->numRows ); max_cell_radius = grid->cellSize.norm()*0.5; //maxi //now enumerate all cells inside this rectangular area for ( iy = iy0; iy<iy1; ++iy ){ for ( ix = ix0; ix < ix1; ++ix ){ //determine, whether this cell can be displayed at all { vec2 c((ix+ftype(0.5))*grid->cellSize.x, (iy+ftype(0.5))*grid->cellSize.y); //distance from the cell center to the circle center ftype d = (c - center).norm(); if ( d > r + max_cell_radius ) continue;//skip this cell - it is too far from center. } //iterate items inside cell curCell = &(grid->cellRef( ix, iy )); iItem = curCell->items.begin(); iItemEnd = curCell->items.end(); while (iItem != iItemEnd){ // for ( iItem = curCell->items.begin(); iItem != curCell->items.end(); ++iItem){ //determine, whether this located item lays inside the circle if ( dist((*iItem)->getPos(), center) <= r ){ YIELD( *iItem, state1 ); } ++iItem; } } } END_GENERATOR; return GridItemPtr();//Dummy }
static void thread1(void *a) { char *name = a; while(!thread_stop_request) { thread_activity_counter++; if(TEST_CHATTY) printf("--- thread %s runs ---\n", name); pressEnter(""); if(TEST_CHATTY) printf("Will lock mutex\n"); hal_mutex_lock(&m); if(TEST_CHATTY) printf("locked mutex\n"); checkEnterMutex(); YIELD(); if(TEST_CHATTY) printf("Will unlock mutex\n"); checkLeaveMutex(); hal_mutex_unlock(&m); if(TEST_CHATTY) printf("unlocked mutex\n"); if( random() & 1 ) hal_sem_acquire( &s ); counter++; if(counter >7) { counter = 0; if(TEST_CHATTY) printf("Will signal cond\n"); hal_cond_signal(&c); if(TEST_CHATTY) printf("Signalled cond\n"); } YIELD(); } FINISH(); }
void GenericCommunicator::send_termination_request( int rcverLocalId, bool isBlocking ) { VT_FUNC_I( "Dist::GenComm::send_termination_request" ); // Send data. No ITAC logging of termination message. if ( ! isBlocking ) { m_sendThread->pushTerminationRequest( rcverLocalId, 0 ); } else { volatile bool myIndicator = false; m_sendThread->pushTerminationRequest( rcverLocalId, &myIndicator ); // Blocking send: wait until the message has been sent successfully: while ( ! myIndicator ) { YIELD(); } } }
unsigned int __stdcall executor(void *arg) { int w; sort(); /* do some work */ InterlockedIncrement(&global_started); for (w = 0; w < LOOP_WORK; w++) { sort(); /* do more work */ if (w % 10 == 0) YIELD(); } InterlockedIncrement(&global_finished); /* TODO: could thread_suspend(a paired thread) */ return 0; }
void input_thread(void* arg) { BYTE ch; lprintf(LOG_DEBUG,"Input thread started"); while(!call_terminated) { if(!comReadByte(com_handle, &ch)) { YIELD(); continue; } if(telnet && ch==TELNET_IAC) sendsocket(sock, &ch, sizeof(ch)); /* escape Telnet IAC char (255) when in telnet mode */ sendsocket(sock, &ch, sizeof(ch)); bytes_received++; } lprintf(LOG_DEBUG,"Input thread terminated"); input_thread_terminated=TRUE; }
Continuation* add(Continuation* co, int a, int b, int * ret) { struct timespec now; VAR_BEGIN struct timespec start; VAR_END if (!this) { *ret = -1; EXIT(); } clock_gettime(CLOCK_REALTIME, &this->start); while(1) { clock_gettime(CLOCK_REALTIME, &now); if ((now.tv_sec - this->start.tv_sec) > 1) break; YIELD(); } *ret = a + b; EXIT(); }
size_t COMIOCALL comReadBuf(COM_HANDLE handle, char* buf, size_t buflen, const char* terminators, int timeout) { BYTE ch; size_t len=0; msclock_t start=msclock(); while(len < buflen) { if(!comReadByte(handle, &ch)) { if(msclock()-start >= timeout) break; YIELD(); continue; } if(len && terminators!=NULL && strchr(terminators, ch)!=NULL) break; buf[len++]=ch; } return len; }
/* * removing a customer from a queue * */ Cust* queueRemoveCust(int debugId, mtQueue *queue) { /*We acquire a lock on the queue and then modify it. A customer is removed and is ready to be served.*/ Cust *cust; int currCustGrp; int currCust; AcquireLock(queueLock[queue->queueType][queue->queueId]); SignalCV(queueLock[queue->queueType][queue->queueId],queueCondVar[queue->queueType][queue->queueId]); ReleaseLock(queueLock[queue->queueType][queue->queueId]); if( queue->queueType == TC_QUEUE ) { while(getMv(&mtCb.tc[queue->queueId].msgFromCust) != CUST_REMOVED) { YIELD(); } setMv(&mtCb.tc[queue->queueId].msgFromCust, INVALID_MSG); currCustGrp = getMv(&mtCb.tc[queue->queueId].currentCustGrpId); currCust = getMv(&mtCb.tc[queue->queueId].currentCustId); cust = &mtCb.custGrp[currCustGrp].cust[currCust]; } AcquireLock(queueLock[queue->queueType][queue->queueId]); if(currCust >= 0 || currCustGrp >= 0) {/*if there were customers in line, we take them out and reduce count of number of customers in queue. After that, we release lock and return pointer to customer*/ getMv(&queue->numCust); setMv(&queue->numCust, queue->numCust.value - 1); ReleaseLock(queueLock[queue->queueType][queue->queueId]); return cust; } else {/*if there were no customers in queue we simply release lock and return a Null signifying no customers*/ ReleaseLock(queueLock[queue->queueType][queue->queueId]); return NULL; } }
DWORD WINAPI ThreadProc(LPVOID lpParam) { int nSleepTime; nSleepTime = (*(int *)lpParam); /* a simple spinning synchronization for the TerminateThread test */ while (thread_proc_wait) { thread_proc_waiting = TRUE; YIELD(); } thread_proc_waiting = FALSE; if (nSleepTime < 5000) { Sleep(nSleepTime); } return 0; }
int threads_execute_request(sb_request_t *sb_req, int thread_id) { unsigned int i; sb_threads_request_t *threads_req = &sb_req->u.threads_request; log_msg_t msg; log_msg_oper_t op_msg; /* Prepare log message */ msg.type = LOG_MSG_TYPE_OPER; msg.data = &op_msg; LOG_EVENT_START(msg, thread_id); for(i = 0; i < thread_yields; i++) { pthread_mutex_lock(&test_mutexes[threads_req->lock_num]); YIELD(); pthread_mutex_unlock(&test_mutexes[threads_req->lock_num]); } LOG_EVENT_STOP(msg, thread_id); return 0; }
BOOL modem_response(COM_HANDLE com_handle, char *str, size_t maxlen) { BYTE ch; size_t len=0; time_t start; lprintf(LOG_DEBUG,"Waiting for Modem Response ..."); start=time(NULL); while(1){ if(time(NULL)-start >= mdm_timeout) { lprintf(LOG_WARNING,"Modem Response TIMEOUT (%lu seconds) on %s", mdm_timeout, com_dev); return FALSE; } if(len >= maxlen) { lprintf(LOG_WARNING,"Modem Response too long (%u >= %u) from %s" ,len, maxlen, com_dev); return FALSE; } if(!comReadByte(com_handle, &ch)) { YIELD(); continue; } if(ch<' ' && len==0) /* ignore prepended control chars */ continue; if(ch=='\r') { while(comReadByte(com_handle,&ch)) /* eat trailing ctrl chars (e.g. 'LF') */ #if 0 lprintf(LOG_DEBUG, "eating ch=%02X", ch) #endif ; break; } str[len++]=ch; } str[len]=0; return TRUE; }