/** * @brief Performs atomic signal and wait operations on two semaphores. * @pre The configuration option @p CH_USE_SEMSW must be enabled in order * to use this function. * * @param[in] sps pointer to a @p Semaphore structure to be signaled * @param[in] spw pointer to a @p Semaphore structure to be wait on * @return A message specifying how the invoking thread has been * released from the semaphore. * @retval RDY_OK if the thread has not stopped on the semaphore or the * semaphore has been signaled. * @retval RDY_RESET if the semaphore has been reset using @p chSemReset(). * * @api */ msg_t chSemSignalWait(Semaphore *sps, Semaphore *spw) { msg_t msg; chDbgCheck((sps != NULL) && (spw != NULL), "chSemSignalWait"); chDbgAssert(((sps->s_cnt >= 0) && isempty(&sps->s_queue)) || ((sps->s_cnt < 0) && notempty(&sps->s_queue)), "chSemSignalWait(), #1", "inconsistent semaphore"); chDbgAssert(((spw->s_cnt >= 0) && isempty(&spw->s_queue)) || ((spw->s_cnt < 0) && notempty(&spw->s_queue)), "chSemSignalWait(), #2", "inconsistent semaphore"); chSysLock(); if (++sps->s_cnt <= 0) chSchReadyI(fifo_remove(&sps->s_queue))->p_u.rdymsg = RDY_OK; if (--spw->s_cnt < 0) { Thread *ctp = currp; sem_insert(ctp, &spw->s_queue); ctp->p_u.wtobjp = spw; chSchGoSleepS(THD_STATE_WTSEM); msg = ctp->p_u.rdymsg; } else { chSchRescheduleS(); msg = RDY_OK; } chSysUnlock(); return msg; }
/** * @brief Signals one thread that is waiting on the condition variable. * @post This function does not reschedule so a call to a rescheduling * function must be performed before unlocking the kernel. Note that * interrupt handlers always reschedule on exit so an explicit * reschedule must not be performed in ISRs. * * @param[in] cp pointer to the @p CondVar structure * * @iclass */ void chCondSignalI(CondVar *cp) { chDbgCheck(cp != NULL, "chCondSignalI"); if (notempty(&cp->c_queue)) chSchReadyI(fifo_remove(&cp->c_queue))->p_u.rdymsg = RDY_OK; }
/** * @brief Reads from a dedicated packet buffer. * * @param[in] udp pointer to a @p stm32_usb_descriptor_t * @param[in] iqp pointer to an @p InputQueue object * @param[in] n maximum number of bytes to copy. This value must * not exceed the maximum packet size for this endpoint. * * @notapi */ static void usb_packet_read_to_queue(stm32_usb_descriptor_t *udp, InputQueue *iqp, size_t n) { size_t nhw; uint32_t *pmap= USB_ADDR2PTR(udp->RXADDR0); nhw = n / 2; while (nhw > 0) { uint32_t w; w = *pmap++; *iqp->q_wrptr++ = (uint8_t)w; if (iqp->q_wrptr >= iqp->q_top) iqp->q_wrptr = iqp->q_buffer; *iqp->q_wrptr++ = (uint8_t)(w >> 8); if (iqp->q_wrptr >= iqp->q_top) iqp->q_wrptr = iqp->q_buffer; nhw--; } /* Last byte for odd numbers.*/ if ((n & 1) != 0) { *iqp->q_wrptr++ = (uint8_t)*pmap; if (iqp->q_wrptr >= iqp->q_top) iqp->q_wrptr = iqp->q_buffer; } /* Updating queue.*/ chSysLockFromIsr(); iqp->q_counter += n; while (notempty(&iqp->q_waiting)) chSchReadyI(fifo_remove(&iqp->q_waiting))->p_u.rdymsg = Q_OK; chSysUnlockFromIsr(); }
/** * @brief Resets an input queue. * @details All the data in the input queue is erased and lost, any waiting * thread is resumed with status @p Q_RESET. * @note A reset operation can be used by a low level driver in order to * obtain immediate attention from the high level layers. * * @param[in] iqp pointer to an @p InputQueue structure * * @iclass */ void chIQResetI(InputQueue *iqp) { chDbgCheckClassI(); iqp->q_rdptr = iqp->q_wrptr = iqp->q_buffer; iqp->q_counter = 0; while (notempty(&iqp->q_waiting)) chSchReadyI(fifo_remove(&iqp->q_waiting))->p_u.rdymsg = Q_RESET; }
/** * @brief Resets an output queue. * @details All the data in the output queue is erased and lost, any waiting * thread is resumed with status @p Q_RESET. * @note A reset operation can be used by a low level driver in order to * obtain immediate attention from the high level layers. * * @param[in] oqp pointer to an @p OutputQueue structure * * @iclass */ void chOQResetI(OutputQueue *oqp) { chDbgCheckClassI(); oqp->q_rdptr = oqp->q_wrptr = oqp->q_buffer; oqp->q_counter = chQSizeI(oqp); while (notempty(&oqp->q_waiting)) chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_RESET; }
/** * @brief Signals one thread that is waiting on the condition variable. * * @param[in] cp pointer to the @p CondVar structure * * @api */ void chCondSignal(CondVar *cp) { chDbgCheck(cp != NULL, "chCondSignal"); chSysLock(); if (notempty(&cp->c_queue)) chSchWakeupS(fifo_remove(&cp->c_queue), RDY_OK); chSysUnlock(); }
/* * Call setdot1 to set up default addresses without ever * setting the previous context mark. */ setdot1() { if (addr2 == 0) addr1 = addr2 = dot; if (addr1 > addr2) { notempty(); error("Addr1 > addr2|First address exceeds second"); } }
void zop(char hadpr) { register int c, lines, op; zhadpr = hadpr; notempty(); znoclear = 0; zweight = 0; switch(c = op = ex_getchar()) { case '^': zweight = 1; case '-': case '+': while (peekchar() == op) { ex_getchar(); zweight++; } case '=': case '.': c = ex_getchar(); break; case EOF: znoclear++; break; default: op = 0; break; } if (digit(c)) { lines = c - '0'; for(;;) { c = ex_getchar(); if (!digit(c)) break; lines *= 10; lines += c - '0'; } if (lines < value(WINDOW)) znoclear++; if (op == '=') lines += 2; } else lines = op == EOF ? value(SCROLL) : value(WINDOW); if (c != EOF) { ungetchar(c); ex_newline(); } addr1 = addr2; setdot(); zop2(lines, op); }
/** * @brief Performs a signal operation on a semaphore. * * @param[in] sp pointer to a @p Semaphore structure * * @api */ void chSemSignal(Semaphore *sp) { chDbgCheck(sp != NULL, "chSemSignal"); chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || ((sp->s_cnt < 0) && notempty(&sp->s_queue)), "chSemSignal(), #1", "inconsistent semaphore"); chSysLock(); if (++sp->s_cnt <= 0) chSchWakeupS(fifo_remove(&sp->s_queue), RDY_OK); chSysUnlock(); }
/* * Call setdot1 to set up default addresses without ever * setting the previous context mark. */ void setdot1(void) { if (addr2 == 0) addr1 = addr2 = dot; if (addr1 > addr2) { notempty(); failed = 1; error(catgets(catd, 1, 6, "Addr1 > addr2|First address exceeds second")); } }
/** * @brief Adds the specified value to the semaphore counter. * @post This function does not reschedule so a call to a rescheduling * function must be performed before unlocking the kernel. Note that * interrupt handlers always reschedule on exit so an explicit * reschedule must not be performed in ISRs. * * @param[in] sp pointer to a @p Semaphore structure * @param[in] n value to be added to the semaphore counter. The value * must be positive. * * @iclass */ void chSemAddCounterI(Semaphore *sp, cnt_t n) { chDbgCheckClassI(); chDbgCheck((sp != NULL) && (n > 0), "chSemAddCounterI"); chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || ((sp->s_cnt < 0) && notempty(&sp->s_queue)), "chSemAddCounterI(), #1", "inconsistent semaphore"); while (n > 0) { if (++sp->s_cnt <= 0) chSchReadyI(fifo_remove(&sp->s_queue))->p_u.rdymsg = RDY_OK; n--; } }
/** * @brief Sets the semaphore counter to the specified value. * @post After invoking this function all the threads waiting on the * semaphore, if any, are released and the semaphore counter is set * to the specified, non negative, value. * @post This function does not reschedule so a call to a rescheduling * function must be performed before unlocking the kernel. Note that * interrupt handlers always reschedule on exit so an explicit * reschedule must not be performed in ISRs. * * @param[in] sp pointer to a @p Semaphore structure * @param[in] n the new value of the semaphore counter. The value must * be non-negative. * * @iclass */ void chSemSetCounterI(Semaphore *sp, cnt_t n) { cnt_t cnt; chDbgCheck((sp != NULL) && (n >= 0), "chSemSetCounterI"); chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || ((sp->s_cnt < 0) && notempty(&sp->s_queue)), "chSemSetCounterI(), #1", "inconsistent semaphore"); cnt = sp->s_cnt; sp->s_cnt = n; while (++cnt <= 0) chSchReadyI(lifo_remove(&sp->s_queue))->p_u.rdymsg = RDY_OK; }
/** * @brief Writes to a TX FIFO fetching data from a queue. * * @param[in] fifop pointer to the FIFO register * @param[in] oqp pointer to an @p OutputQueue object * @param[in] n maximum number of bytes to copy * * @notapi */ static void otg_fifo_write_from_queue(volatile uint32_t *fifop, OutputQueue *oqp, size_t n) { size_t ntogo; ntogo = n; while (ntogo > 0) { uint32_t w, i; size_t nw = ntogo / 4; if (nw > 0) { size_t streak; uint32_t nw2end = (oqp->q_top - oqp->q_rdptr) / 4; ntogo -= (streak = nw <= nw2end ? nw : nw2end) * 4; oqp->q_rdptr = otg_do_push(fifop, oqp->q_rdptr, streak); if (oqp->q_rdptr >= oqp->q_top) { oqp->q_rdptr = oqp->q_buffer; continue; } } /* If this condition is not satisfied then there is a word lying across queue circular buffer boundary or there are some remaining bytes.*/ if (ntogo <= 0) break; /* One byte at time.*/ w = 0; i = 0; while ((ntogo > 0) && (i < 4)) { w |= (uint32_t)*oqp->q_rdptr++ << (i * 8); if (oqp->q_rdptr >= oqp->q_top) oqp->q_rdptr = oqp->q_buffer; ntogo--; i++; } *fifop = w; } /* Updating queue.*/ chSysLock(); oqp->q_counter += n; while (notempty(&oqp->q_waiting)) chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK; chSchRescheduleS(); chSysUnlock(); }
/** * @brief Reads a packet from the RXFIFO. * * @param[in] fifop pointer to the FIFO register * @param[in] iqp pointer to an @p InputQueue object * @param[in] n number of bytes to pull from the FIFO * * @notapi */ static void otg_fifo_read_to_queue(volatile uint32_t *fifop, InputQueue *iqp, size_t n) { size_t ntogo; ntogo = n; while (ntogo > 0) { uint32_t w, i; size_t nw = ntogo / 4; if (nw > 0) { size_t streak; uint32_t nw2end = (iqp->q_wrptr - iqp->q_wrptr) / 4; ntogo -= (streak = nw <= nw2end ? nw : nw2end) * 4; iqp->q_wrptr = otg_do_pop(fifop, iqp->q_wrptr, streak); if (iqp->q_wrptr >= iqp->q_top) { iqp->q_wrptr = iqp->q_buffer; continue; } } /* If this condition is not satisfied then there is a word lying across queue circular buffer boundary or there are some remaining bytes.*/ if (ntogo <= 0) break; /* One byte at time.*/ w = *fifop; i = 0; while ((ntogo > 0) && (i < 4)) { *iqp->q_wrptr++ = (uint8_t)(w >> (i * 8)); if (iqp->q_wrptr >= iqp->q_top) iqp->q_wrptr = iqp->q_buffer; ntogo--; i++; } } /* Updating queue.*/ chSysLock(); iqp->q_counter += n; while (notempty(&iqp->q_waiting)) chSchReadyI(fifo_remove(&iqp->q_waiting))->p_u.rdymsg = Q_OK; chSchRescheduleS(); chSysUnlock(); }
/** * @brief Performs a signal operation on a semaphore. * @post This function does not reschedule so a call to a rescheduling * function must be performed before unlocking the kernel. Note that * interrupt handlers always reschedule on exit so an explicit * reschedule must not be performed in ISRs. * * @param[in] sp pointer to a @p Semaphore structure * * @iclass */ void chSemSignalI(Semaphore *sp) { chDbgCheck(sp != NULL, "chSemSignalI"); chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || ((sp->s_cnt < 0) && notempty(&sp->s_queue)), "chSemSignalI(), #1", "inconsistent semaphore"); if (++sp->s_cnt <= 0) { /* note, it is done this way in order to allow a tail call on chSchReadyI().*/ Thread *tp = fifo_remove(&sp->s_queue); tp->p_u.rdymsg = RDY_OK; chSchReadyI(tp); } }
/** * @brief Performs a wait operation on a semaphore. * * @param[in] sp pointer to a @p Semaphore structure * @return A message specifying how the invoking thread has been * released from the semaphore. * @retval RDY_OK if the thread has not stopped on the semaphore or the * semaphore has been signaled. * @retval RDY_RESET if the semaphore has been reset using @p chSemReset(). * * @sclass */ msg_t chSemWaitS(Semaphore *sp) { chDbgCheck(sp != NULL, "chSemWaitS"); chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || ((sp->s_cnt < 0) && notempty(&sp->s_queue)), "chSemWaitS(), #1", "inconsistent semaphore"); if (--sp->s_cnt < 0) { currp->p_u.wtobjp = sp; sem_insert(currp, &sp->s_queue); chSchGoSleepS(THD_STATE_WTSEM); return currp->p_u.rdymsg; } return RDY_OK; }
/** * @brief Input queue write. * @details A byte value is written into the low end of an input queue. * * @param[in] iqp pointer to an @p InputQueue structure * @param[in] b the byte value to be written in the queue * @return The operation status. * @retval Q_OK if the operation has been completed with success. * @retval Q_FULL if the queue is full and the operation cannot be * completed. * * @iclass */ msg_t chIQPutI(InputQueue *iqp, uint8_t b) { chDbgCheckClassI(); if (chIQIsFullI(iqp)) return Q_FULL; iqp->q_counter++; *iqp->q_wrptr++ = b; if (iqp->q_wrptr >= iqp->q_top) iqp->q_wrptr = iqp->q_buffer; if (notempty(&iqp->q_waiting)) chSchReadyI(fifo_remove(&iqp->q_waiting))->p_u.rdymsg = Q_OK; return Q_OK; }
/** * @brief Output queue read. * @details A byte value is read from the low end of an output queue. * * @param[in] oqp pointer to an @p OutputQueue structure * @return The byte value from the queue. * @retval Q_EMPTY if the queue is empty. * * @iclass */ msg_t chOQGetI(OutputQueue *oqp) { uint8_t b; chDbgCheckClassI(); if (chOQIsEmptyI(oqp)) return Q_EMPTY; oqp->q_counter++; b = *oqp->q_rdptr++; if (oqp->q_rdptr >= oqp->q_top) oqp->q_rdptr = oqp->q_buffer; if (notempty(&oqp->q_waiting)) chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK; return b; }
/** * @brief Terminates the current thread. * @details The thread goes in the @p THD_STATE_FINAL state holding the * specified exit status code, other threads can retrieve the * exit status code by invoking the function @p chThdWait(). * @post Eventual code after this function will never be executed, * this function never returns. The compiler has no way to * know this so do not assume that the compiler would remove * the dead code. * * @param[in] msg thread exit code * * @api */ void chThdExit(msg_t msg) { Thread *tp = currp; chSysLock(); tp->p_u.exitcode = msg; #if defined(THREAD_EXT_EXIT_HOOK) THREAD_EXT_EXIT_HOOK(tp); #endif #if CH_USE_WAITEXIT while (notempty(&tp->p_waiting)) chSchReadyI(list_remove(&tp->p_waiting)); #endif #if CH_USE_REGISTRY /* Static threads are immediately removed from the registry because there is no memory to recover.*/ if ((tp->p_flags & THD_MEM_MODE_MASK) == THD_MEM_MODE_STATIC) REG_REMOVE(tp); #endif chSchGoSleepS(THD_STATE_FINAL); }
/** * @brief Performs a wait operation on a semaphore with timeout specification. * * @param[in] sp pointer to a @p Semaphore structure * @param[in] time the number of ticks before the operation timeouts, * the following special values are allowed: * - @a TIME_IMMEDIATE immediate timeout. * - @a TIME_INFINITE no timeout. * . * @return A message specifying how the invoking thread has been * released from the semaphore. * @retval RDY_OK if the thread has not stopped on the semaphore or the * semaphore has been signaled. * @retval RDY_RESET if the semaphore has been reset using @p chSemReset(). * @retval RDY_TIMEOUT if the semaphore has not been signaled or reset within * the specified timeout. * * @sclass */ msg_t chSemWaitTimeoutS(Semaphore *sp, systime_t time) { chDbgCheck(sp != NULL, "chSemWaitTimeoutS"); chDbgAssert(((sp->s_cnt >= 0) && isempty(&sp->s_queue)) || ((sp->s_cnt < 0) && notempty(&sp->s_queue)), "chSemWaitTimeoutS(), #1", "inconsistent semaphore"); if (--sp->s_cnt < 0) { if (TIME_IMMEDIATE == time) { sp->s_cnt++; return RDY_TIMEOUT; } currp->p_u.wtobjp = sp; sem_insert(currp, &sp->s_queue); return chSchGoSleepTimeoutS(THD_STATE_WTSEM, time); } return RDY_OK; }
/** * @brief Terminates the current thread. * @details The thread goes in the @p THD_STATE_FINAL state holding the * specified exit status code, other threads can retrieve the * exit status code by invoking the function @p chThdWait(). * @post Eventual code after this function will never be executed, * this function never returns. The compiler has no way to * know this so do not assume that the compiler would remove * the dead code. * * @param[in] msg thread exit code * * @sclass */ void chThdExitS(msg_t msg) { Thread *tp = currp; tp->p_u.exitcode = msg; #if defined(THREAD_EXT_EXIT_HOOK) THREAD_EXT_EXIT_HOOK(tp); #endif #if CH_USE_WAITEXIT while (notempty(&tp->p_waiting)) chSchReadyI(list_remove(&tp->p_waiting)); #endif #if CH_USE_REGISTRY /* Static threads are immediately removed from the registry because there is no memory to recover.*/ if ((tp->p_flags & THD_MEM_MODE_MASK) == THD_MEM_MODE_STATIC) REG_REMOVE(tp); #endif chSchGoSleepS(THD_STATE_FINAL); /* The thread never returns here.*/ chDbgAssert(FALSE, "chThdExitS(), #1", "zombies apocalypse"); }
/** * @brief Writes to a dedicated packet buffer. * * @param[in] udp pointer to a @p stm32_usb_descriptor_t * @param[in] buf buffer where to fetch the packet data * @param[in] n maximum number of bytes to copy. This value must * not exceed the maximum packet size for this endpoint. * * @notapi */ static void usb_packet_write_from_queue(stm32_usb_descriptor_t *udp, OutputQueue *oqp, size_t n) { size_t nhw; uint32_t *pmap = USB_ADDR2PTR(udp->TXADDR0); udp->TXCOUNT0 = (uint16_t)n; nhw = n / 2; while (nhw > 0) { uint32_t w; w = (uint32_t)*oqp->q_rdptr++; if (oqp->q_rdptr >= oqp->q_top) oqp->q_rdptr = oqp->q_buffer; w |= (uint32_t)*oqp->q_rdptr++ << 8; if (oqp->q_rdptr >= oqp->q_top) oqp->q_rdptr = oqp->q_buffer; *pmap++ = w; nhw--; } /* Last byte for odd numbers.*/ if ((n & 1) != 0) { *pmap = (uint32_t)*oqp->q_rdptr++; if (oqp->q_rdptr >= oqp->q_top) oqp->q_rdptr = oqp->q_buffer; } /* Updating queue. Note, the lock is done in this unusual way because this function can be called from both ISR and thread context so the kind of lock function to be invoked cannot be decided beforehand.*/ port_lock(); dbg_enter_lock(); oqp->q_counter += n; while (notempty(&oqp->q_waiting)) chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK; dbg_leave_lock(); port_unlock(); }
int main(int argc, char** argv) { int i; if(argc < 4) errx(-1, "missing arguments"); int nproc = atoi(argv[1]); char* inputname = argv[2]; char* outputname = argv[3]; int inputfd = openx(inputname, O_RDONLY); int outputfd = openx(outputname, O_RDWR | O_CREAT); off_t inputsize = argc > 4 ? parsesize(argv[4]) : statfdsize(inputfd, inputname); off_t outputsize = DISTINCT_CRC_VALUES * sizeof(count_t); uint8_t* input = mapinput(inputfd, inputsize, inputname); count_t* output = mapoutput(outputfd, outputsize, outputname); pid_t children[nproc]; pid_t dead; for(i = 0; i < nproc; i++) if((children[i] = spawnworker(i, nproc, input, inputsize, output)) < 0) break; int status; while(notempty(children, nproc)) if((dead = wait(&status)) > 0) if(handledead(children, nproc, dead, status)) break; terminate(children, nproc); return messy(children, nproc) ? -1 : 0; }
/* * Main loop for command mode command decoding. * A few commands are executed here, but main function * is to strip command addresses, do a little address oriented * processing and call command routines to do the real work. */ void commands(bool noprompt, bool exitoneof) { register line *addr; register int c; register int lchng; int given; int seensemi; int cnt; bool hadpr; resetflav(); nochng(); for (;;) { /* * If dot at last command * ended up at zero, advance to one if there is a such. */ if (dot <= zero) { dot = zero; if (dol > zero) dot = one; } shudclob = 0; /* * If autoprint or trailing print flags, * print the line at the specified offset * before the next command. */ if (pflag || (lchng != chng && value(AUTOPRINT) && !inglobal && !inopen && endline)) { pflag = 0; nochng(); if (dol != zero) { addr1 = addr2 = dot + poffset; if (addr1 < one || addr1 > dol) error("Offset out-of-bounds|Offset after command too large"); setdot1(); goto print; } } nochng(); /* * Print prompt if appropriate. * If not in global flush output first to prevent * going into pfast mode unreasonably. */ if (inglobal == 0) { flush(); if (!hush && value(PROMPT) && !globp && !noprompt && endline) { ex_putchar(':'); hadpr = 1; } TSYNC(); } /* * Gobble up the address. * Degenerate addresses yield ".". */ addr2 = 0; given = seensemi = 0; do { addr1 = addr2; addr = address(0); c = getcd(); if (addr == 0) { if (c == ',') addr = dot; else if (addr1 != 0) { addr2 = dot; break; } else break; } addr2 = addr; given++; if (c == ';') { c = ','; dot = addr; seensemi = 1; } } while (c == ','); if (c == '%') { /* %: same as 1,$ */ addr1 = one; addr2 = dol; given = 2; c = ex_getchar(); } if (addr1 == 0) addr1 = addr2; if (c == ':') c = ex_getchar(); /* * Set command name for special character commands. */ tailspec(c); /* * If called via : escape from open or visual, limit * the set of available commands here to save work below. */ if (inopen) { if (c=='\n' || c=='\r' || c==CTRL('d') || c==EOF) { if (addr2) dot = addr2; if (c == EOF) return; continue; } if (any(c, "o")) notinvis: tailprim(Command, 1, 1); } switch (c) { case 'a': switch(peekchar()) { case 'b': /* abbreviate */ tail("abbreviate"); setnoaddr(); mapcmd(0, 1); anyabbrs = 1; continue; case 'r': /* args */ tail("args"); setnoaddr(); eol(); pargs(); continue; } /* append */ if (inopen) goto notinvis; tail("append"); setdot(); aiflag = exclam(); ex_newline(); vmacchng(0); deletenone(); setin(addr2); inappend = 1; ignore(append(gettty, addr2)); inappend = 0; nochng(); continue; case 'c': switch (peekchar()) { /* copy */ case 'o': tail("copy"); vmacchng(0); move(); continue; #ifdef CHDIR /* cd */ case 'd': tail("cd"); goto changdir; /* chdir */ case 'h': ignchar(); if (peekchar() == 'd') { register char *p; tail2of("chdir"); changdir: if (savedfile[0] == '/' || !value(WARN)) ignore(exclam()); else ignore(quickly()); if (skipend()) { p = getenv("HOME"); if (p == NULL) error("Home directory unknown"); } else getone(), p = file; eol(); if (chdir(p) < 0) filioerr(p); if (savedfile[0] != '/') edited = 0; continue; } if (inopen) tailprim("change", 2, 1); tail2of("change"); break; #endif default: if (inopen) goto notinvis; tail("change"); break; } /* change */ aiflag = exclam(); setCNL(); vmacchng(0); setin(addr1); delete(0); inappend = 1; ignore(append(gettty, addr1 - 1)); inappend = 0; nochng(); continue; /* delete */ case 'd': /* * Caution: dp and dl have special meaning already. */ tail("delete"); c = cmdreg(); setCNL(); vmacchng(0); if (c) YANKreg(c); delete(0); appendnone(); continue; /* edit */ /* ex */ case 'e': tail(peekchar() == 'x' ? "ex" : "edit"); editcmd: if (!exclam() && chng) c = 'E'; filename(c); if (c == 'E') { ungetchar(lastchar()); ignore(quickly()); } setnoaddr(); doecmd: init(); addr2 = zero; laste++; ex_sync(); rop(c); nochng(); continue; /* file */ case 'f': tail("file"); setnoaddr(); filename(c); noonl(); /* synctmp(); */ continue; /* global */ case 'g': tail("global"); global(!exclam()); nochng(); continue; /* insert */ case 'i': if (inopen) goto notinvis; tail("insert"); setdot(); nonzero(); aiflag = exclam(); ex_newline(); vmacchng(0); deletenone(); setin(addr2); inappend = 1; ignore(append(gettty, addr2 - 1)); inappend = 0; if (dot == zero && dol > zero) dot = one; nochng(); continue; /* join */ case 'j': tail("join"); c = exclam(); setcount(); nonzero(); ex_newline(); vmacchng(0); if (given < 2 && addr2 != dol) addr2++; join(c); continue; /* k */ case 'k': casek: pastwh(); c = ex_getchar(); if (endcmd(c)) serror("Mark what?|%s requires following letter", Command); ex_newline(); if (!islower(c)) error("Bad mark|Mark must specify a letter"); setdot(); nonzero(); names[c - 'a'] = *addr2 &~ 01; anymarks = 1; continue; /* list */ case 'l': tail("list"); setCNL(); ignorf(setlist(1)); pflag = 0; goto print; case 'm': if (peekchar() == 'a') { ignchar(); if (peekchar() == 'p') { /* map */ tail2of("map"); setnoaddr(); mapcmd(0, 0); continue; } /* mark */ tail2of("mark"); goto casek; } /* move */ tail("move"); vmacchng(0); move(); continue; case 'n': if (peekchar() == 'u') { tail("number"); goto numberit; } /* next */ tail("next"); setnoaddr(); ckaw(); ignore(quickly()); if (getargs()) makargs(); next(); c = 'e'; filename(c); goto doecmd; /* open */ case 'o': tail("open"); oop(); pflag = 0; nochng(); continue; case 'p': case 'P': switch (peekchar()) { /* put */ case 'u': tail("put"); setdot(); c = cmdreg(); eol(); vmacchng(0); if (c) putreg(c); else put(); continue; case 'r': ignchar(); if (peekchar() == 'e') { /* preserve */ tail2of("preserve"); eol(); if (preserve() == 0) error("Preserve failed!"); else error("File preserved."); } tail2of("print"); break; default: tail("print"); break; } /* print */ setCNL(); pflag = 0; print: nonzero(); if (CL && span() > EX_LINES) { flush1(); vclear(); } plines(addr1, addr2, 1); continue; /* quit */ case 'q': tail("quit"); setnoaddr(); c = quickly(); eol(); if (!c) quit: nomore(); if (inopen) { vgoto(WECHO, 0); if (!ateopr()) vnfl(); else { tostop(); } flush(); setty(normf); } cleanup(1); ex_exit(0); case 'r': if (peekchar() == 'e') { ignchar(); switch (peekchar()) { /* rewind */ case 'w': tail2of("rewind"); setnoaddr(); if (!exclam()) { ckaw(); if (chng && dol > zero) error("No write@since last chage (:rewind! overrides)"); } eol(); erewind(); next(); c = 'e'; ungetchar(lastchar()); filename(c); goto doecmd; /* recover */ case 'c': tail2of("recover"); setnoaddr(); c = 'e'; if (!exclam() && chng) c = 'E'; filename(c); if (c == 'E') { ungetchar(lastchar()); ignore(quickly()); } init(); addr2 = zero; laste++; ex_sync(); recover(); rop2(); revocer(); if (status == 0) rop3(c); if (dol != zero) change(); nochng(); continue; } tail2of("read"); } else tail("read"); /* read */ if (savedfile[0] == 0 && dol == zero) c = 'e'; pastwh(); vmacchng(0); if (peekchar() == '!') { setdot(); ignchar(); unix0(0); filter(0); continue; } filename(c); rop(c); nochng(); if (inopen && endline && addr1 > zero && addr1 < dol) dot = addr1 + 1; continue; case 's': switch (peekchar()) { /* * Caution: 2nd char cannot be c, g, or r * because these have meaning to substitute. */ /* set */ case 'e': tail("set"); setnoaddr(); set(); continue; /* shell */ case 'h': tail("shell"); setNAEOL(); vnfl(); putpad(TE); flush(); unixwt(1, unixex("-i", (char *) 0, 0, 0)); vcontin(0); continue; /* source */ case 'o': #ifdef notdef if (inopen) goto notinvis; #endif tail("source"); setnoaddr(); getone(); eol(); source(file, 0); continue; #ifdef SIGTSTP /* stop, suspend */ case 't': tail("stop"); goto suspend; case 'u': tail("suspend"); suspend: if (!dosusp) error("Old tty driver|Not using new tty driver/shell"); c = exclam(); eol(); if (!c) ckaw(); onsusp(0); continue; #endif } /* fall into ... */ /* & */ /* ~ */ /* substitute */ case '&': case '~': Command = "substitute"; if (c == 's') tail(Command); vmacchng(0); if (!substitute(c)) pflag = 0; continue; /* t */ case 't': if (peekchar() == 'a') { tail("tag"); tagfind(exclam()); if (!inopen) lchng = chng - 1; else nochng(); continue; } tail("t"); vmacchng(0); move(); continue; case 'u': if (peekchar() == 'n') { ignchar(); switch(peekchar()) { /* unmap */ case 'm': tail2of("unmap"); setnoaddr(); mapcmd(1, 0); continue; /* unabbreviate */ case 'a': tail2of("unabbreviate"); setnoaddr(); mapcmd(1, 1); anyabbrs = 1; continue; } /* undo */ tail2of("undo"); } else tail("undo"); setnoaddr(); markDOT(); c = exclam(); ex_newline(); undo(c); continue; case 'v': switch (peekchar()) { case 'e': /* version */ tail("version"); setNAEOL(); ex_printf("@(#) Version 3.6, 11/3/80" " (4.0BSD). git " "160803 14:24" +5); noonl(); continue; /* visual */ case 'i': tail("visual"); if (inopen) { c = 'e'; goto editcmd; } vop(); pflag = 0; nochng(); continue; } /* v */ tail("v"); global(0); nochng(); continue; /* write */ case 'w': c = peekchar(); tail(c == 'q' ? "wq" : "write"); wq: if (skipwh() && peekchar() == '!') { pofix(); ignchar(); setall(); unix0(0); filter(1); } else { setall(); wop(1); nochng(); } if (c == 'q') goto quit; continue; /* xit */ case 'x': tail("xit"); if (!chng) goto quit; c = 'q'; goto wq; /* yank */ case 'y': tail("yank"); c = cmdreg(); setcount(); eol(); vmacchng(0); if (c) YANKreg(c); else yank(); continue; /* z */ case 'z': zop(0); pflag = 0; continue; /* * */ /* @ */ case '*': case '@': c = ex_getchar(); if (c=='\n' || c=='\r') ungetchar(c); if (any(c, "@*\n\r")) c = lastmac; if (isupper(c)) c = tolower(c); if (!islower(c)) error("Bad register"); ex_newline(); setdot(); cmdmac(c); continue; /* | */ case '|': endline = 0; goto caseline; /* \n */ case '\n': endline = 1; caseline: notempty(); if (addr2 == 0) { if (UP != NOSTR && c == '\n' && !inglobal) c = CTRL('k'); if (inglobal) addr1 = addr2 = dot; else { if (dot == dol) error("At EOF|At end-of-file"); addr1 = addr2 = dot + 1; } } setdot(); nonzero(); if (seensemi) addr1 = addr2; ex_getline(*addr1); if (c == CTRL('k')) { flush1(); destline--; if (hadpr) shudclob = 1; } plines(addr1, addr2, 1); continue; /* " */ case '"': comment(); continue; /* # */ case '#': numberit: setCNL(); ignorf(setnumb(1)); pflag = 0; goto print; /* = */ case '=': ex_newline(); setall(); if (inglobal == 2) pofix(); ex_printf("%d", lineno(addr2)); noonl(); continue; /* ! */ case '!': if (addr2 != 0) { vmacchng(0); unix0(0); setdot(); filter(2); } else { unix0(1); pofix(); putpad(TE); flush(); unixwt(1, unixex("-c", uxb, 0, 0)); vclrech(1); /* vcontin(0); */ nochng(); } continue; /* < */ /* > */ case '<': case '>': for (cnt = 1; peekchar() == c; cnt++) ignchar(); setCNL(); vmacchng(0); shift(c, cnt); continue; /* ^D */ /* EOF */ case CTRL('d'): case EOF: if (exitoneof) { if (addr2 != 0) dot = addr2; return; } if (!isatty(0)) { if (intty) /* * Chtty sys call at UCB may cause a * input which was a tty to suddenly be * turned into /dev/null. */ onhup(0); return; } if (addr2 != 0) { setlastchar('\n'); putnl(); } if (dol == zero) { if (addr2 == 0) putnl(); notempty(); } ungetchar(EOF); zop(hadpr); continue; default: if (!isalpha(c)) break; ungetchar(c); tailprim("", 0, 0); } ierror("What?|Unknown command character '%c'", c); } }
/* * Parse an address. * Just about any sequence of address characters is legal. * * If you are tricky you can use this routine and the = command * to do simple addition and subtraction of cardinals less * than the number of lines in the file. */ line * address(char *inputline) { register line *addr; register int offset, c; short lastsign; bigmove = 0; lastsign = 0; offset = 0; addr = 0; for (;;) { if (isdigit(peekcd())) { if (addr == 0) { addr = zero; bigmove = 1; } loc1 = 0; addr += offset; offset = getnum(); if (lastsign >= 0) addr += offset; else addr -= offset; lastsign = 0; offset = 0; } switch (c = getcd()) { case '?': case '/': case '$': case '\'': case '\\': bigmove++; case '.': if (addr || offset) error("Badly formed address"); } offset += lastsign; lastsign = 0; switch (c) { case ' ': case '\t': continue; case '+': lastsign = 1; if (addr == 0) addr = dot; continue; case '^': case '-': lastsign = -1; if (addr == 0) addr = dot; continue; case '\\': case '?': case '/': c = compile(c, 1); notempty(); savere(scanre); addr = dot; if (inputline && execute(0, dot)) { if (c == '/') { while (loc1 <= inputline) { if (loc1 == loc2) loc2++; if (!execute(1, NULL)) goto nope; } break; } else if (loc1 < inputline) { char *last; doques: do { last = loc1; if (loc1 == loc2) loc2++; if (!execute(1, NULL)) break; } while (loc1 < inputline); loc1 = last; break; } } nope: for (;;) { if (c == '/') { addr++; if (addr > dol) { if (value(WRAPSCAN) == 0) error("No match to BOTTOM|Address search hit BOTTOM without matching pattern"); addr = zero; } } else { addr--; if (addr < zero) { if (value(WRAPSCAN) == 0) error("No match to TOP|Address search hit TOP without matching pattern"); addr = dol; } } if (execute(0, addr)) { if (inputline && c == '?') { inputline = &linebuf[LBSIZE]; goto doques; } break; } if (addr == dot) error("Fail|Pattern not found"); } continue; case '$': addr = dol; continue; case '.': addr = dot; continue; case '\'': c = markreg(ex_getchar()); if (c == 0) error("Marks are ' and a-z"); addr = getmark(c); if (addr == 0) error("Undefined mark@referenced"); break; default: ungetchar(c); if (offset) { if (addr == 0) addr = dot; addr += offset; loc1 = 0; } if (addr == 0) { bigmove = 0; return (0); } if (addr != zero) notempty(); addr += lastsign; if (addr < zero) error("Negative address@- first buffer line is 1"); if (addr > dol) error("Not that many lines@in buffer"); return (addr); } } }