int initializeClient(const char *name) { int sock, rc; sockaddr_x dag; socklen_t daglen; char sdag[1024]; char IP[MAX_XID_SIZE]; // lookup the xia service daglen = sizeof(dag); if (XgetDAGbyName(name, &dag, &daglen) < 0) die(-1, "unable to locate: %s\n", name); // create a socket, and listen for incoming connections if ((sock = Xsocket(AF_XIA, SOCK_STREAM, 0)) < 0) die(-1, "Unable to create the listening socket\n"); if (Xconnect(sock, (struct sockaddr*)&dag, daglen) < 0) { Xclose(sock); die(-1, "Unable to bind to the dag: %s\n", dag); } rc = XreadLocalHostAddr(sock, my_ad, MAX_XID_SIZE, my_hid, MAX_XID_SIZE, IP, MAX_XID_SIZE); if (rc < 0) { Xclose(sock); die(-1, "Unable to read local address.\n"); } else{ warn("My AD: %s, My HID: %s\n", my_ad, my_hid); } // save the AD and HID for later. This seems hacky // we need to find a better way to deal with this Graph g(&dag); strncpy(sdag, g.dag_string().c_str(), sizeof(sdag)); // say("sdag = %s\n",sdag); char *ads = strstr(sdag,"AD:"); char *hids = strstr(sdag,"HID:"); // i = sscanf(ads,"%s",s_ad ); // i = sscanf(hids,"%s", s_hid); if(sscanf(ads,"%s",s_ad ) < 1 || strncmp(s_ad,"AD:", 3) !=0){ die(-1, "Unable to extract AD."); } if(sscanf(hids,"%s", s_hid) < 1 || strncmp(s_hid,"HID:", 4) !=0 ){ die(-1, "Unable to extract AD."); } warn("Service AD: %s, Service HID: %s\n", s_ad, s_hid); return sock; }
TEST(XreadLocalHostAddr, InvalidPointers) { int sock = Xsocket(AF_XIA, XSOCK_STREAM, 0); int rc = XreadLocalHostAddr(sock, NULL, XID_LEN, NULL, XID_LEN, NULL, XID_LEN); ASSERT_EQ(-1, rc); Xclose(sock); }
//Just registering the service and openning the necessary sockets int registerReceiver() { int sock; say ("\n%s (%s): started\n", TITLE, VERSION); // create a socket, and listen for incoming connections if ((sock = Xsocket(AF_XIA, SOCK_STREAM, 0)) < 0) die(-1, "Unable to create the listening socket\n"); // read the localhost AD and HID if ( XreadLocalHostAddr(sock, myAD, sizeof(myAD), myHID, sizeof(myHID), my4ID, sizeof(my4ID)) < 0 ) die(-1, "Reading localhost address\n"); struct addrinfo *ai; //FIXME: SID is hardcoded if (Xgetaddrinfo(NULL, SID, NULL, &ai) != 0) die(-1, "getaddrinfo failure!\n"); sockaddr_x *dag = (sockaddr_x*)ai->ai_addr; //FIXME NAME is hard coded if (XregisterName(NAME, dag) < 0 ) die(-1, "error registering name: %s\n", NAME); if (Xbind(sock, (struct sockaddr*)dag, sizeof(dag)) < 0) { Xclose(sock); die(-1, "Unable to bind to the dag: %s\n", dag); } Graph g(dag); say("listening on dag: %s\n", g.dag_string().c_str()); return sock; }
void echo_dgram() { int sock; char buf[XIA_MAXBUF]; sockaddr_x cdag; socklen_t dlen; int n; say("Datagram service started\n"); if ((sock = Xsocket(AF_XIA, SOCK_DGRAM, 0)) < 0) die(-2, "unable to create the datagram socket\n"); struct addrinfo *ai; if (Xgetaddrinfo(NULL, SID_DGRAM, NULL, &ai) != 0) die(-1, "getaddrinfo failure!\n"); sockaddr_x *sa = (sockaddr_x*)ai->ai_addr; Graph g((sockaddr_x*)ai->ai_addr); printf("\nDatagram DAG\n%s\n", g.dag_string().c_str()); if (XregisterName(DGRAM_NAME, sa) < 0 ) die(-1, "error registering name: %s\n", DGRAM_NAME); say("registered name: \n%s\n", DGRAM_NAME); if (Xbind(sock, (sockaddr *)sa, sizeof(sa)) < 0) { die(-3, "unable to bind to the dag\n"); } pid_t pid = 0; // only need to fork if doing stream echo at the same time if (stream == 1) pid = fork(); if (pid == 0) { while (1) { say("Dgram Server waiting\n"); dlen = sizeof(cdag); memset(buf, 0, sizeof(buf)); if ((n = Xrecvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&cdag, &dlen)) < 0) { warn("Recv error on socket %d, closing connection\n", pid); break; } say("dgram received %d bytes\n", n); if ((n = Xsendto(sock, buf, n, 0, (struct sockaddr *)&cdag, dlen)) < 0) { warn("%5d send error\n", pid); break; } say("dgram sent %d bytes\n", n); } Xclose(sock); } }
void handler(int) { timetodie = 1; if (sock > 0) Xclose(sock); sock = -1; }
/* * Save an array subscript - returns true if matching bracket found, false * if eof or newline was found. * (Returned string double null terminated) */ static bool arraysub(char **strp) { XString ws; char *wp, c; /* we are just past the initial [ */ unsigned int depth = 1; Xinit(ws, wp, 32, ATEMP); do { c = getsc(); Xcheck(ws, wp); *wp++ = c; if (c == '[') depth++; else if (c == ']') depth--; } while (depth > 0 && c && c != '\n'); *wp++ = '\0'; *strp = Xclose(ws, wp); return (tobool(depth == 0)); }
/* * Save an array subscript - returns true if matching bracket found, false * if eof or newline was found. * (Returned string double null terminated) */ static int arraysub(char **strp) { XString ws; char *wp; char c; int depth = 1; /* we are just past the initial [ */ Xinit(ws, wp, 32, ATEMP); do { c = getsc(); Xcheck(ws, wp); *wp++ = c; if (c == '[') depth++; else if (c == ']') depth--; } while (depth > 0 && c && c != '\n'); *wp++ = '\0'; *strp = Xclose(ws, wp); return depth == 0 ? 1 : 0; }
int getFile(int sock, char *p_ad, char* p_hid, const char *fin, const char *fout) { int chunkSock; int offset; char cmd[512]; char reply[512]; int status = 0; //TODO: check the arguments to be correct // send the file request sprintf(cmd, "get %s", fin); sendCmd(sock, cmd); // get back number of chunks in the file if (getChunkCount(sock, reply, sizeof(reply)) < 1){ warn("could not get chunk count. Aborting. \n"); return -1; } int count = atoi(&reply[4]); if ((chunkSock = Xsocket(AF_XIA, XSOCK_CHUNK, 0)) < 0) die(-1, "unable to create chunk socket\n"); FILE *f = fopen(fout, "w"); offset = 0; while (offset < count) { int num = NUM_CHUNKS; if (count - offset < num) num = count - offset; // tell the server we want a list of <num> cids starting at location <offset> sprintf(cmd, "block %d:%d", offset, num); sendCmd(sock, cmd); if (getChunkCount(sock, reply, sizeof(reply)) < 1){ warn("could not get chunk count. Aborting. \n"); return -1; } offset += NUM_CHUNKS; if (getListedChunks(chunkSock, f, &reply[4], p_ad, p_hid) < 0) { status= -1; break; } } fclose(f); if (status < 0) { unlink(fin); } say("Received file %s\n", fout); sendCmd(sock, "done"); Xclose(chunkSock); return status; }
static void readhere(struct ioword *iop) { int c; char *volatile eof; char *eofp; int skiptabs; XString xs; char *xp; int xpos; eof = evalstr(iop->delim, 0); if (!(iop->flag & IOEVAL)) ignore_backslash_newline++; Xinit(xs, xp, 256, ATEMP); for (;;) { eofp = eof; skiptabs = iop->flag & IOSKIP; xpos = Xsavepos(xs, xp); while ((c = getsc()) != 0) { if (skiptabs) { if (c == '\t') continue; skiptabs = 0; } if (c != *eofp) break; Xcheck(xs, xp); Xput(xs, xp, c); eofp++; } /* Allow EOF here so commands with out trailing newlines * will work (eg, ksh -c '...', $(...), etc). */ if (*eofp == '\0' && (c == 0 || c == '\n')) { xp = Xrestpos(xs, xp, xpos); break; } ungetsc(c); while ((c = getsc()) != '\n') { if (c == 0) yyerror("here document `%s' unclosed\n", eof); Xcheck(xs, xp); Xput(xs, xp, c); } Xcheck(xs, xp); Xput(xs, xp, c); } Xput(xs, xp, '\0'); iop->heredoc = Xclose(xs, xp); if (!(iop->flag & IOEVAL)) ignore_backslash_newline--; }
/* * search for command with PATH */ const char * search_path(const char *name, const char *lpath, /* R_OK or X_OK */ int mode, /* set if candidate found, but not suitable */ int *errnop) { const char *sp, *p; char *xp; XString xs; size_t namelen; int ec = 0, ev; if (vstrchr(name, '/')) { if ((ec = search_access(name, mode)) == 0) { search_path_ok: if (errnop) *errnop = 0; return (name); } goto search_path_err; } namelen = strlen(name) + 1; Xinit(xs, xp, 128, ATEMP); sp = lpath; while (sp != NULL) { xp = Xstring(xs, xp); if (!(p = cstrchr(sp, ':'))) p = sp + strlen(sp); if (p != sp) { XcheckN(xs, xp, p - sp); memcpy(xp, sp, p - sp); xp += p - sp; *xp++ = '/'; } sp = p; XcheckN(xs, xp, namelen); memcpy(xp, name, namelen); if ((ev = search_access(Xstring(xs, xp), mode)) == 0) { name = Xclose(xs, xp + namelen); goto search_path_ok; } /* accumulate non-ENOENT errors only */ if (ev != ENOENT && ec == 0) ec = ev; if (*sp++ == '\0') sp = NULL; } Xfree(xs, xp); search_path_err: if (errnop) *errnop = ec ? ec : ENOENT; return (NULL); }
void integerGet(int opt, int val) { int s = Xsocket(AF_XIA, SOCK_DGRAM, 0); socklen_t sz = sizeof(int); int v = 999; EXPECT_EQ(0, Xsetsockopt(sock, opt, (const void *)&val, sz)); EXPECT_EQ(0, Xgetsockopt(sock, opt, (void *)&v, &sz)); EXPECT_EQ(sizeof(int), sz); EXPECT_EQ(v, val); Xclose(s); }
/* ** get data from the client and return it ** run in a child process that was forked off from the main process. */ void process(int sock) { char buf[XIA_MAXBUF + 1]; int n; pid_t pid = getpid(); fd_set fds; FD_ZERO(&fds); FD_SET(sock, &fds); #ifdef USE_SELECT struct timeval tv; tv.tv_sec = WAIT_FOR_DATA; tv.tv_usec = 0; #endif while (1) { memset(buf, 0, sizeof(buf)); #ifdef USE_SELECT tv.tv_sec = WAIT_FOR_DATA; tv.tv_usec = 0; if ((n = Xselect(sock + 1, &fds, NULL, NULL, &tv)) < 0) { warn("%5d Select failed, closing...\n", pid); break; } else if (n == 0) { // we timed out, close the socket say("%5d timed out on recv\n", pid); break; } else if (!FD_ISSET(sock, &fds)) { // this shouldn't happen! die(-4, "something is really wrong, exiting\n"); } #endif if ((n = Xrecv(sock, buf, sizeof(buf), 0)) < 0) { warn("Recv error on socket %d, closing connection\n", pid); break; } else if (n == 0) { warn("%d client closed the connection\n", pid); break; } say("%5d received %d bytes\n", pid, n); if ((n = Xsend(sock, buf, n, 0)) < 0) { warn("%5d send error\n", pid); break; } say("%5d sent %d bytes\n", pid, n); } say("%5d closing\n", pid); Xclose(sock); }
int sendCmd(int sock, const char *cmd) { int n; warn("Sending Command: %s \n", cmd); if ((n = Xsend(sock, cmd, strlen(cmd), 0)) < 0) { Xclose(sock); die(-1, "Unable to communicate\n"); } return n; }
int sendCmd(int sock, const char *cmd) { int n; if ((n = Xsend(sock, cmd, strlen(cmd), 0)) < 0) { Xclose(sock); die(-1, "Unable to communicate with the server\n"); } return n; }
/* ** the main loop for talking to the remote server ** when threading is enabled, one of these will run in each thread ** ** The parameter and return code are there to satisify the thread library */ void *mainLoop(void * /* dummy */) { int ssock; int count = 0; int printcount = 1; // don't bother with the loop count if we are ony doing one operation if (loops == 1) printcount = 0; ssock = connectToServer(); for (;;) { if (printcount) printf("Xsock %4d loop #%d\n", ssock, count); if (process(ssock) != 0) break; pausex(); count++; if (reconnect) { if (count % reconnect == 0) { // time to close and reopen the socket Xclose(ssock); say("Xsock %4d closed\n", ssock); ssock = connectToServer(); } } if (loops > 0 && count == loops) break; } Xclose(ssock); say("Xsock %4d closed\n", ssock); return NULL; }
int main(int argc, char *argv[]) { int sock, n; size_t dlen; char reply[128]; char buffer[2048],theirDAG[1024]; //Open socket sock=Xsocket(XSOCK_DGRAM); if (sock < 0) error("Opening socket"); //Name query to the name server char * sname = (char*) malloc(snprintf(NULL, 0, "%s", SNAME) + 1); sprintf(sname, "%s", SNAME); char * dag = XgetDAGbyName(sname); while(1) { printf("\nPlease enter the message (0 to exit): "); bzero(buffer,2048); fgets(buffer,2048,stdin); if (buffer[0]=='0'&&strlen(buffer)==2) break; //Use Xconnect() with Xsend() //Xsend(sock,buffer,strlen(buffer),0); //Or use Xsendto() Xsendto(sock,buffer,strlen(buffer),0,dag,strlen(dag)+1); printf("Sent\n"); //Process reply from server dlen = sizeof(theirDAG); n = Xrecvfrom(sock,reply,128,0,theirDAG,&dlen); //n = Xrecv(sock,reply,128,0); if (n < 0) error("recvfrom"); //printf("Received a datagram from:%s\n",theirDAG); write(1,reply,n); printf("\n"); } Xclose(sock); return 0; }
int getChunkCount(int sock, char *reply, int sz) { int n; if ((n = Xrecv(sock, reply, sz, 0)) < 0) { Xclose(sock); die(-1, "Unable to communicate with the server\n"); } if (strncmp(reply, "OK:", 3) != 0) { die(-1, "%s\n", reply); } reply[n] = 0; return n; }
char * get_phys_path(const char *path) { XString xs; char *xp; Xinit(xs, xp, strlen(path) + 1, ATEMP); xp = do_phys_path(&xs, xp, path); if (!xp) return ((char *) 0); if (Xlength(xs, xp) == 0) Xput(xs, xp, '/'); Xput(xs, xp, '\0'); return (Xclose(xs, xp)); }
void *blockingListener(void *socketid) { int sock = *((int*)socketid); int acceptSock; while (1) { say("Waiting for a client connection\n"); if ((acceptSock = Xaccept(sock, NULL, NULL)) < 0) die(-1, "accept failed\n"); say("connected\n"); // handle the connection in a new thread pthread_t client; pthread_create(&client, NULL, recvCmd, (void *)&acceptSock); } Xclose(sock); // we should never reach here! return NULL; }
virtual void SetUp() { sockaddr_x sa; int sock = Xsocket(AF_XIA, SOCK_STREAM, 0); XreadLocalHostAddr(sock, ad, XID_LEN, hid, XID_LEN, fid, XID_LEN); sprintf(fdag, FULL_DAG, ad, hid, SID); Graph gf(fdag); gf.fill_sockaddr(&sa); XregisterName(FULL_NAME, &sa); sprintf(hdag, HOST_DAG, ad, hid); Graph gh(hdag); gh.fill_sockaddr(&sa); XregisterName(HOST_NAME, &sa); Xclose(sock); nad = new Node(ad); nhid = new Node(hid); nsid = new Node(SID); ai = NULL; }
virtual void TearDown() { Xclose(sock); }
/*! ** @brief Register a service or hostname with the name server. ** ** Register a host or service name with the XIA nameserver. ** By convention services are indicated by '_s' appended to the service name. ** The memory returned is dynamically allocated and should be released with a ** call to free() when the caller is done with it. ** ** This is a very simple implementation and will be replaced in a ** future release. This version does not check correctness of the name or dag, ** nor does it check to ensure that the client is allowed to bind to name. ** ** @param name - The name of an XIA service or host. ** @param DAG - the DAG to be bound to name. ** ** @returns 0 on success ** @returns -1 on failure with errno set ** */ int XregisterName(const char *name, sockaddr_x *DAG) { int sock; sockaddr_x ns_dag; char pkt[NS_MAX_PACKET_SIZE]; char _name[NS_MAX_DAG_LENGTH], _dag[NS_MAX_DAG_LENGTH]; int result; if ((sock = Xsocket(AF_XIA, SOCK_DGRAM, 0)) < 0) return -1; //Read the nameserver DAG (the one that the name-registration will be sent to) if (XreadNameServerDAG(sock, &ns_dag) < 0) { LOG("Unable to find nameserver address"); errno = NO_RECOVERY; return -1; } if (!DAG) { errno = EINVAL; return -1; } Graph g(DAG); if (g.num_nodes() <= 0) { errno = EINVAL; return -1; } //Construct a registration packet ns_pkt register_pkt; register_pkt.type = NS_TYPE_REGISTER; register_pkt.name = strdup(name); register_pkt.dag = strdup(g.dag_string().c_str()); memset(pkt, 0, sizeof(pkt)); int offset = 0; memcpy(pkt, ®ister_pkt.type, sizeof(register_pkt.type)); offset += sizeof(register_pkt.type); memcpy(pkt+offset, register_pkt.name, strlen(register_pkt.name)+1); offset += strlen(register_pkt.name)+1; memcpy(pkt+offset, register_pkt.dag, strlen(register_pkt.dag)+1); offset += strlen(register_pkt.dag)+1; //Send the name registration packet to the name server //FIXME: use sockaddr here Xsendto(sock, pkt, offset, 0, (const struct sockaddr *)&ns_dag, sizeof(sockaddr_x)); //Check the response from the name server memset(pkt, 0, sizeof(pkt)); int rc = Xrecvfrom(sock, pkt, NS_MAX_PACKET_SIZE, 0, NULL, NULL); if (rc < 0) { perror("recvfrom"); } memset(_name, '\0', NS_MAX_DAG_LENGTH); memset(_dag, '\0', NS_MAX_DAG_LENGTH); ns_pkt *tmp = (ns_pkt *)pkt; char* tmp_name = (char*)(pkt+sizeof(tmp->type)); char* tmp_dag = (char*)(pkt+sizeof(tmp->type)+ strlen(tmp_name)+1); switch (tmp->type) { case NS_TYPE_RESPONSE: sprintf(_name, "%s", tmp_name); sprintf(_dag, "%s", tmp_dag); result = 0; break; case NS_TYPE_RESPONSE_ERROR: result = -1; break; default: fprintf(stderr, "dafault\n"); result = -1; break; } free(register_pkt.name); free(register_pkt.dag); Xclose(sock); return result; }
/*! ** @brief Lookup a DAG based using a host or service name. ** ** The name should be a string such as www_s.example.xia or host.example.xia. ** By convention services are indicated by '_s' appended to the service name. ** The memory returned is dynamically allocated and should be released with a ** call to free() when the caller is done with it. ** ** This is a very simple implementation of the name query function. ** It will be replaces in a future release. ** ** @param name The name of an XIA service or host. ** ** @returns a character point to the dag on success ** @returns NULL on failure ** */ int XgetDAGbyName(const char *name, sockaddr_x *addr, socklen_t *addrlen) { int sock; sockaddr_x ns_dag; char pkt[NS_MAX_PACKET_SIZE]; char *dag; char _name[NS_MAX_DAG_LENGTH], _dag[NS_MAX_DAG_LENGTH]; int result; if (!addr || !addrlen || *addrlen < sizeof(sockaddr_x)) { errno = EINVAL; return -1; } // see if name is registered in the local hosts.xia file if((dag = hostsLookup(name))) { Graph g(dag); free(dag); // check to see if the returned dag was valid // we may want a better check for this in the future if (g.num_nodes() > 0) { std::string s = g.dag_string(); g.fill_sockaddr((sockaddr_x*)addr); *addrlen = sizeof(sockaddr_x); return 0; } } // not found locally, check the name server if ((sock = Xsocket(AF_XIA, SOCK_DGRAM, 0)) < 0) return -1; //Read the nameserver DAG (the one that the name-query will be sent to) if ( XreadNameServerDAG(sock, &ns_dag) < 0 ) { LOG("Unable to find nameserver address"); errno = NO_RECOVERY; return -1; } //Construct a name-query packet ns_pkt query_pkt; query_pkt.type = NS_TYPE_QUERY; query_pkt.name = strdup(name); memset(pkt, 0, sizeof(pkt)); int offset = 0; memcpy(pkt, &query_pkt.type, sizeof(query_pkt.type)); offset += sizeof(query_pkt.type); memcpy(pkt+offset, query_pkt.name, strlen(query_pkt.name)+1); offset += strlen(query_pkt.name)+1; //Send a name query to the name server Xsendto(sock, pkt, offset, 0, (const struct sockaddr*)&ns_dag, sizeof(sockaddr_x)); //Check the response from the name server memset(pkt, 0, sizeof(pkt)); int rc = Xrecvfrom(sock, pkt, NS_MAX_PACKET_SIZE, 0, NULL, NULL); if (rc < 0) { perror("recvfrom"); } memset(_name, '\0', NS_MAX_DAG_LENGTH); memset(_dag, '\0', NS_MAX_DAG_LENGTH); ns_pkt *tmp = (ns_pkt *)pkt; char* tmp_name = (char*)(pkt+sizeof(tmp->type)); char* tmp_dag = (char*)(pkt+sizeof(tmp->type)+ strlen(tmp_name)+1); switch (tmp->type) { case NS_TYPE_RESPONSE: sprintf(_name, "%s", tmp_name); sprintf(_dag, "%s", tmp_dag); result = 1; break; case NS_TYPE_RESPONSE_ERROR: result = -1; break; default: LOG("Unknown nameserver response"); result = -1; break; } Xclose(sock); free(query_pkt.name); if (result < 0) { return result; } Graph g(_dag); g.fill_sockaddr(addr); return 0; }
int main(int argc, char **argv) { struct addrinfo *ai; sockaddr_x *sa; int seq = 0; // FIXME: put signal handlers back into code once Xselect is working // signal(SIGINT, handler); // signal(SIGTERM, handler); getConfig(argc, argv); if (Xgetaddrinfo(NAME, NULL, NULL, &ai) < 0) die("Unable to lookup address for %s\n", NAME); sa = (sockaddr_x*)ai->ai_addr; if ((sock = Xsocket(AF_XIA, SOCK_STREAM, 0)) < 0) die("Unable to create a socket\n"); say("Opening firehose: %s\n", NAME); if (Xconnect(sock, (struct sockaddr*)sa, sizeof(sockaddr_x)) < 0) { Xclose(sock); die("Unable to connect to %s\n", NAME); } // tell firehose how many packets we expect if (Xsend(sock, &fhc, sizeof(fhc), 0) < 0) { Xclose(sock); die("Unable to send config information to the firehose\n"); } int count = 0; char *buf = (char *)malloc(pktSize); while (!timetodie) { int n; fd_set fds; FD_ZERO(&fds); FD_SET(sock, &fds); struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0; if ((n = Xselect(sock + 1, &fds, NULL, NULL, &tv)) < 0) { printf("select failed\n"); break; } else if (n == 0) { printf("recv timeout\n"); break; } else if (!FD_ISSET(sock, &fds)) { // this shouldn't happen! printf("something is really wrong, exiting\n"); break; } int rc = Xrecv(sock, buf, sizeof(buf), 0); if (rc < 0) die("Receive failure\n"); memcpy(&seq, buf, sizeof(int)); say("expecting %d, got %d", count, seq); if (count == seq) say("\n"); else say(" lost %d\n", seq - count); count++; if (count == numPkts) break; if (delay) usleep(delay); } seq++; if (count != seq) printf("lost %d packets, received %d, expected %d\n", seq - count, count, seq); else printf("success!\n"); Xclose(sock); }
int yylex(int cf) { Lex_state states[STATE_BSIZE], *statep, *s2, *base; State_info state_info; int c, c2, state; size_t cz; XString ws; /* expandable output word */ char *wp; /* output word pointer */ char *sp, *dp; Again: states[0].type = SINVALID; states[0].ls_base = NULL; statep = &states[1]; state_info.base = states; state_info.end = &state_info.base[STATE_BSIZE]; Xinit(ws, wp, 64, ATEMP); backslash_skip = 0; ignore_backslash_newline = 0; if (cf & ONEWORD) state = SWORD; else if (cf & LETEXPR) { /* enclose arguments in (double) quotes */ *wp++ = OQUOTE; state = SLETPAREN; statep->nparen = 0; } else { /* normal lexing */ state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; while ((c = getsc()) == ' ' || c == '\t') ; if (c == '#') { ignore_backslash_newline++; while ((c = getsc()) != '\0' && c != '\n') ; ignore_backslash_newline--; } ungetsc(c); } if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ source->flags &= ~SF_ALIAS; /* POSIX: trailing space only counts if parsing simple cmd */ if (!Flag(FPOSIX) || (cf & CMDWORD)) cf |= ALIAS; } /* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */ statep->type = state; /* collect non-special or quoted characters to form word */ while (!((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { if (state == SBASE && subshell_nesting_type == /*{*/ '}' && c == /*{*/ '}') /* possibly end ${ :;} */ break; Xcheck(ws, wp); switch (state) { case SADELIM: if (c == '(') statep->nparen++; else if (c == ')') statep->nparen--; else if (statep->nparen == 0 && (c == /*{*/ '}' || c == (int)statep->ls_adelim.delimiter)) { *wp++ = ADELIM; *wp++ = c; if (c == /*{*/ '}' || --statep->ls_adelim.num == 0) POP_STATE(); if (c == /*{*/ '}') POP_STATE(); break; } /* FALLTHROUGH */ case SBASE: if (c == '[' && (cf & (VARASN|ARRAYVAR))) { /* temporary */ *wp = EOS; if (is_wdvarname(Xstring(ws, wp), false)) { char *p, *tmp; if (arraysub(&tmp)) { *wp++ = CHAR; *wp++ = c; for (p = tmp; *p; ) { Xcheck(ws, wp); *wp++ = CHAR; *wp++ = *p++; } afree(tmp, ATEMP); break; } else { Source *s; s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = tmp; s->next = source; source = s; } } *wp++ = CHAR; *wp++ = c; break; } /* FALLTHROUGH */ Sbase1: /* includes *(...|...) pattern (*+?@!) */ if (c == '*' || c == '@' || c == '+' || c == '?' || c == '!') { c2 = getsc(); if (c2 == '(' /*)*/ ) { *wp++ = OPAT; *wp++ = c; PUSH_STATE(SPATTERN); break; } ungetsc(c2); } /* FALLTHROUGH */ Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ switch (c) { case '\\': getsc_qchar: if ((c = getsc())) { /* trailing \ is lost */ *wp++ = QCHAR; *wp++ = c; } break; case '\'': open_ssquote_unless_heredoc: if ((cf & HEREDOC)) goto store_char; *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SSQUOTE); break; case '"': open_sdquote: *wp++ = OQUOTE; PUSH_STATE(SDQUOTE); break; case '$': /* * processing of dollar sign belongs into * Subst, except for those which can open * a string: $'…' and $"…" */ subst_dollar_ex: c = getsc(); switch (c) { case '"': goto open_sdquote; case '\'': goto open_sequote; default: goto SubstS; } default: goto Subst; } break; Subst: switch (c) { case '\\': c = getsc(); switch (c) { case '"': if ((cf & HEREDOC)) goto heredocquote; /* FALLTHROUGH */ case '\\': case '$': case '`': store_qchar: *wp++ = QCHAR; *wp++ = c; break; default: heredocquote: Xcheck(ws, wp); if (c) { /* trailing \ is lost */ *wp++ = CHAR; *wp++ = '\\'; *wp++ = CHAR; *wp++ = c; } break; } break; case '$': c = getsc(); SubstS: if (c == '(') /*)*/ { c = getsc(); if (c == '(') /*)*/ { *wp++ = EXPRSUB; PUSH_SRETRACE(SASPAREN); statep->nparen = 2; *retrace_info->xp++ = '('; } else { ungetsc(c); subst_command: c = COMSUB; subst_command2: sp = yyrecursive(c); cz = strlen(sp) + 1; XcheckN(ws, wp, cz); *wp++ = c; memcpy(wp, sp, cz); wp += cz; } } else if (c == '{') /*}*/ { if ((c = getsc()) == '|') { /* * non-subenvironment * value substitution */ c = VALSUB; goto subst_command2; } else if (ctype(c, C_IFSWS)) { /* * non-subenvironment * "command" substitution */ c = FUNSUB; goto subst_command2; } ungetsc(c); *wp++ = OSUBST; *wp++ = '{'; /*}*/ wp = get_brace_var(&ws, wp); c = getsc(); /* allow :# and :% (ksh88 compat) */ if (c == ':') { *wp++ = CHAR; *wp++ = c; c = getsc(); if (c == ':') { *wp++ = CHAR; *wp++ = '0'; *wp++ = ADELIM; *wp++ = ':'; PUSH_STATE(SBRACE); PUSH_STATE(SADELIM); statep->ls_adelim.delimiter = ':'; statep->ls_adelim.num = 1; statep->nparen = 0; break; } else if (ksh_isdigit(c) || c == '('/*)*/ || c == ' ' || /*XXX what else? */ c == '$') { /* substring subst. */ if (c != ' ') { *wp++ = CHAR; *wp++ = ' '; } ungetsc(c); PUSH_STATE(SBRACE); PUSH_STATE(SADELIM); statep->ls_adelim.delimiter = ':'; statep->ls_adelim.num = 2; statep->nparen = 0; break; } } else if (c == '/') { *wp++ = CHAR; *wp++ = c; if ((c = getsc()) == '/') { *wp++ = ADELIM; *wp++ = c; } else ungetsc(c); PUSH_STATE(SBRACE); PUSH_STATE(SADELIM); statep->ls_adelim.delimiter = '/'; statep->ls_adelim.num = 1; statep->nparen = 0; break; } /* * If this is a trim operation, * treat (,|,) specially in STBRACE. */ if (ctype(c, C_SUBOP2)) { ungetsc(c); if (Flag(FSH)) PUSH_STATE(STBRACEBOURNE); else PUSH_STATE(STBRACEKORN); } else { ungetsc(c); if (state == SDQUOTE || state == SQBRACE) PUSH_STATE(SQBRACE); else PUSH_STATE(SBRACE); } } else if (ksh_isalphx(c)) { *wp++ = OSUBST; *wp++ = 'X'; do { Xcheck(ws, wp); *wp++ = c; c = getsc(); } while (ksh_isalnux(c)); *wp++ = '\0'; *wp++ = CSUBST; *wp++ = 'X'; ungetsc(c); } else if (ctype(c, C_VAR1 | C_DIGIT)) { Xcheck(ws, wp); *wp++ = OSUBST; *wp++ = 'X'; *wp++ = c; *wp++ = '\0'; *wp++ = CSUBST; *wp++ = 'X'; } else { *wp++ = CHAR; *wp++ = '$'; ungetsc(c); } break; case '`': subst_gravis: PUSH_STATE(SBQUOTE); *wp++ = COMSUB; /* * We need to know whether we are within double * quotes, since most shells translate \" to " * within "…`…\"…`…". This is not done in POSIX * mode (§2.2.3 Double-Quotes: “The backquote * shall retain its special meaning introducing * the other form of command substitution (see * Command Substitution). The portion of the * quoted string from the initial backquote and * the characters up to the next backquote that * is not preceded by a <backslash>, having * escape characters removed, defines that * command whose output replaces "`...`" when * the word is expanded.”; §2.6.3 Command * Substitution: “Within the backquoted style * of command substitution, <backslash> shall * retain its literal meaning, except when * followed by: '$', '`', or <backslash>. The * search for the matching backquote shall be * satisfied by the first unquoted non-escaped * backquote; during this search, if a * non-escaped backquote is encountered[…], * undefined results occur.”). */ statep->ls_bool = false; if (Flag(FPOSIX)) break; s2 = statep; base = state_info.base; while (/* CONSTCOND */ 1) { for (; s2 != base; s2--) { if (s2->type == SDQUOTE) { statep->ls_bool = true; break; } } if (s2 != base) break; if (!(s2 = s2->ls_base)) break; base = s2-- - STATE_BSIZE; } break; case QCHAR: if (cf & LQCHAR) { *wp++ = QCHAR; *wp++ = getsc(); break; } /* FALLTHROUGH */ default: store_char: *wp++ = CHAR; *wp++ = c; } break; case SEQUOTE: if (c == '\'') { POP_STATE(); *wp++ = CQUOTE; ignore_backslash_newline--; } else if (c == '\\') { if ((c2 = unbksl(true, s_get, s_put)) == -1) c2 = s_get(); if (c2 == 0) statep->ls_bool = true; if (!statep->ls_bool) { char ts[4]; if ((unsigned int)c2 < 0x100) { *wp++ = QCHAR; *wp++ = c2; } else { cz = utf_wctomb(ts, c2 - 0x100); ts[cz] = 0; cz = 0; do { *wp++ = QCHAR; *wp++ = ts[cz]; } while (ts[++cz]); } } } else if (!statep->ls_bool) { *wp++ = QCHAR; *wp++ = c; } break; case SSQUOTE: if (c == '\'') { POP_STATE(); if ((cf & HEREDOC) || state == SQBRACE) goto store_char; *wp++ = CQUOTE; ignore_backslash_newline--; } else { *wp++ = QCHAR; *wp++ = c; } break; case SDQUOTE: if (c == '"') { POP_STATE(); *wp++ = CQUOTE; } else goto Subst; break; /* $(( ... )) */ case SASPAREN: if (c == '(') statep->nparen++; else if (c == ')') { statep->nparen--; if (statep->nparen == 1) { /* end of EXPRSUB */ POP_SRETRACE(); if ((c2 = getsc()) == /*(*/ ')') { cz = strlen(sp) - 2; XcheckN(ws, wp, cz); memcpy(wp, sp + 1, cz); wp += cz; afree(sp, ATEMP); *wp++ = '\0'; break; } else { Source *s; ungetsc(c2); /* * mismatched parenthesis - * assume we were really * parsing a $(...) expression */ --wp; s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = sp; s->next = source; source = s; goto subst_command; } } } /* reuse existing state machine */ goto Sbase2; case SQBRACE: if (c == '\\') { /* * perform POSIX "quote removal" if the back- * slash is "special", i.e. same cases as the * {case '\\':} in Subst: plus closing brace; * in mksh code "quote removal" on '\c' means * write QCHAR+c, otherwise CHAR+\+CHAR+c are * emitted (in heredocquote:) */ if ((c = getsc()) == '"' || c == '\\' || c == '$' || c == '`' || c == /*{*/'}') goto store_qchar; goto heredocquote; } goto common_SQBRACE; case SBRACE: if (c == '\'') goto open_ssquote_unless_heredoc; else if (c == '\\') goto getsc_qchar; common_SQBRACE: if (c == '"') goto open_sdquote; else if (c == '$') goto subst_dollar_ex; else if (c == '`') goto subst_gravis; else if (c != /*{*/ '}') goto store_char; POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; break; /* Same as SBASE, except (,|,) treated specially */ case STBRACEKORN: if (c == '|') *wp++ = SPAT; else if (c == '(') { *wp++ = OPAT; /* simile for @ */ *wp++ = ' '; PUSH_STATE(SPATTERN); } else /* FALLTHROUGH */ case STBRACEBOURNE: if (c == /*{*/ '}') { POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; } else goto Sbase1; break; case SBQUOTE: if (c == '`') { *wp++ = 0; POP_STATE(); } else if (c == '\\') { switch (c = getsc()) { case 0: /* trailing \ is lost */ break; case '$': case '`': case '\\': *wp++ = c; break; case '"': if (statep->ls_bool) { *wp++ = c; break; } /* FALLTHROUGH */ default: *wp++ = '\\'; *wp++ = c; break; } } else *wp++ = c; break; /* ONEWORD */ case SWORD: goto Subst; /* LETEXPR: (( ... )) */ case SLETPAREN: if (c == /*(*/ ')') { if (statep->nparen > 0) --statep->nparen; else if ((c2 = getsc()) == /*(*/ ')') { c = 0; *wp++ = CQUOTE; goto Done; } else { Source *s; ungetsc(c2); /* * mismatched parenthesis - * assume we were really * parsing a (...) expression */ *wp = EOS; sp = Xstring(ws, wp); dp = wdstrip(sp + 1, WDS_TPUTS); s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = dp; s->next = source; source = s; return ('('/*)*/); } } else if (c == '(') /* * parentheses inside quotes and * backslashes are lost, but AT&T ksh * doesn't count them either */ ++statep->nparen; goto Sbase2; /* << or <<- delimiter */ case SHEREDELIM: /* * here delimiters need a special case since * $ and `...` are not to be treated specially */ switch (c) { case '\\': if ((c = getsc())) { /* trailing \ is lost */ *wp++ = QCHAR; *wp++ = c; } break; case '\'': goto open_ssquote_unless_heredoc; case '$': if ((c2 = getsc()) == '\'') { open_sequote: *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SEQUOTE); statep->ls_bool = false; break; } else if (c2 == '"') { /* FALLTHROUGH */ case '"': PUSH_SRETRACE(SHEREDQUOTE); break; } ungetsc(c2); /* FALLTHROUGH */ default: *wp++ = CHAR; *wp++ = c; } break; /* " in << or <<- delimiter */ case SHEREDQUOTE: if (c != '"') goto Subst; POP_SRETRACE(); dp = strnul(sp) - 1; /* remove the trailing double quote */ *dp = '\0'; /* store the quoted string */ *wp++ = OQUOTE; XcheckN(ws, wp, (dp - sp) * 2); dp = sp; while ((c = *dp++)) { if (c == '\\') { switch ((c = *dp++)) { case '\\': case '"': case '$': case '`': break; default: *wp++ = CHAR; *wp++ = '\\'; break; } } *wp++ = CHAR; *wp++ = c; } afree(sp, ATEMP); *wp++ = CQUOTE; state = statep->type = SHEREDELIM; break; /* in *(...|...) pattern (*+?@!) */ case SPATTERN: if (c == /*(*/ ')') { *wp++ = CPAT; POP_STATE(); } else if (c == '|') { *wp++ = SPAT; } else if (c == '(') { *wp++ = OPAT; /* simile for @ */ *wp++ = ' '; PUSH_STATE(SPATTERN); } else goto Sbase1; break; } } Done: Xcheck(ws, wp); if (statep != &states[1]) /* XXX figure out what is missing */ yyerror("no closing quote\n"); /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ if (state == SHEREDELIM) state = SBASE; dp = Xstring(ws, wp); if (state == SBASE && ( #ifndef MKSH_LEGACY_MODE (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) || #endif c == '<' || c == '>')) { struct ioword *iop = alloc(sizeof(struct ioword), ATEMP); if (Xlength(ws, wp) == 0) iop->unit = c == '<' ? 0 : 1; else for (iop->unit = 0, c2 = 0; c2 < Xlength(ws, wp); c2 += 2) { if (dp[c2] != CHAR) goto no_iop; if (!ksh_isdigit(dp[c2 + 1])) goto no_iop; iop->unit = iop->unit * 10 + ksh_numdig(dp[c2 + 1]); if (iop->unit >= FDBASE) goto no_iop; } if (c == '&') { if ((c2 = getsc()) != '>') { ungetsc(c2); goto no_iop; } c = c2; iop->ioflag = IOBASH; } else iop->ioflag = 0; c2 = getsc(); /* <<, >>, <> are ok, >< is not */ if (c == c2 || (c == '<' && c2 == '>')) { iop->ioflag |= c == c2 ? (c == '>' ? IOCAT : IOHERE) : IORDWR; if (iop->ioflag == IOHERE) { if ((c2 = getsc()) == '-') iop->ioflag |= IOSKIP; else if (c2 == '<') iop->ioflag |= IOHERESTR; else ungetsc(c2); } } else if (c2 == '&') iop->ioflag |= IODUP | (c == '<' ? IORDUP : 0); else { iop->ioflag |= c == '>' ? IOWRITE : IOREAD; if (c == '>' && c2 == '|') iop->ioflag |= IOCLOB; else ungetsc(c2); } iop->ioname = NULL; iop->delim = NULL; iop->heredoc = NULL; /* free word */ Xfree(ws, wp); yylval.iop = iop; return (REDIR); no_iop: afree(iop, ATEMP); } if (wp == dp && state == SBASE) { /* free word */ Xfree(ws, wp); /* no word, process LEX1 character */ if ((c == '|') || (c == '&') || (c == ';') || (c == '('/*)*/)) { if ((c2 = getsc()) == c) c = (c == ';') ? BREAK : (c == '|') ? LOGOR : (c == '&') ? LOGAND : /* c == '(' ) */ MDPAREN; else if (c == '|' && c2 == '&') c = COPROC; else if (c == ';' && c2 == '|') c = BRKEV; else if (c == ';' && c2 == '&') c = BRKFT; else ungetsc(c2); #ifndef MKSH_SMALL if (c == BREAK) { if ((c2 = getsc()) == '&') c = BRKEV; else ungetsc(c2); } #endif } else if (c == '\n') { if (cf & HEREDELIM) ungetsc(c); else { gethere(); if (cf & CONTIN) goto Again; } } return (c); } /* terminate word */ *wp++ = EOS; yylval.cp = Xclose(ws, wp); if (state == SWORD || state == SLETPAREN /* XXX ONEWORD? */) return (LWORD); /* unget terminator */ ungetsc(c); /* * note: the alias-vs-function code below depends on several * interna: starting from here, source->str is not modified; * the way getsc() and ungetsc() operate; etc. */ /* copy word to unprefixed string ident */ sp = yylval.cp; dp = ident; while ((dp - ident) < IDENT && (c = *sp++) == CHAR) *dp++ = *sp++; if (c != EOS) /* word is not unquoted */ dp = ident; /* make sure the ident array stays NUL padded */ memset(dp, 0, (ident + IDENT) - dp + 1); if (!(cf & (KEYWORD | ALIAS))) return (LWORD); if (*ident != '\0') { struct tbl *p; uint32_t h = hash(ident); if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == /*{*/ '}')) { afree(yylval.cp, ATEMP); return (p->val.i); } if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && (p->flag & ISSET)) { /* * this still points to the same character as the * ungetsc'd terminator from above */ const char *cp = source->str; /* prefer POSIX but not Korn functions over aliases */ while (*cp == ' ' || *cp == '\t') /* * this is like getsc() without skipping * over Source boundaries (including not * parsing ungetsc'd characters that got * pushed into an SREREAD) which is what * we want here anyway: find out whether * the alias name is followed by a POSIX * function definition */ ++cp; /* prefer functions over aliases */ if (cp[0] != '(' || cp[1] != ')') { Source *s = source; while (s && (s->flags & SF_HASALIAS)) if (s->u.tblp == p) return (LWORD); else s = s->next; /* push alias expansion */ s = pushs(SALIAS, source->areap); s->start = s->str = p->val.s; s->u.tblp = p; s->flags |= SF_HASALIAS; s->next = source; if (source->type == SEOF) { /* prevent infinite recursion at EOS */ source->u.tblp = p; source->flags |= SF_HASALIAS; } source = s; afree(yylval.cp, ATEMP); goto Again; } } } else if (cf & ALIAS) { /* retain typeset et al. even when quoted */ if (assign_command((dp = wdstrip(yylval.cp, 0)))) strlcpy(ident, dp, sizeof(ident)); afree(dp, ATEMP); } return (LWORD); }
int yylex(int cf) { Lex_state states[STATE_BSIZE], *statep; State_info state_info; int c, state; XString ws; /* expandable output word */ char *wp; /* output word pointer */ char *sp, *dp; int c2; Again: states[0].ls_state = -1; states[0].ls_info.base = (Lex_state *) 0; statep = &states[1]; state_info.base = states; state_info.end = &states[STATE_BSIZE]; Xinit(ws, wp, 64, ATEMP); backslash_skip = 0; ignore_backslash_newline = 0; if (cf&ONEWORD) state = SWORD; else if (cf&LETEXPR) { *wp++ = OQUOTE; /* enclose arguments in (double) quotes */ state = SLETPAREN; statep->ls_sletparen.nparen = 0; } else { /* normal lexing */ state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; while ((c = getsc()) == ' ' || c == '\t') ; if (c == '#') { ignore_backslash_newline++; while ((c = getsc()) != '\0' && c != '\n') ; ignore_backslash_newline--; } ungetsc(c); } if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ source->flags &= ~SF_ALIAS; /* In POSIX mode, a trailing space only counts if we are * parsing a simple command */ if (!Flag(FPOSIX) || (cf & CMDWORD)) cf |= ALIAS; } /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */ statep->ls_state = state; /* collect non-special or quoted characters to form word */ while (!((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { Xcheck(ws, wp); switch (state) { case SBASE: if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) && c == '!') { char **replace = NULL; c2 = getsc(); if (c2 == '\0') ; else if (c2 == ' ' || c2 == '\t') ungetsc(c2); else if (c2 == '!') replace = hist_get_newest(0); else if (isdigit(c2) || c2 == '-' || isalpha(c2)) { int get = !isalpha(c2); char match[200], *str = match; *str++ = c2; do { if ((c2 = getsc()) == '\0') break; if (c2 == '\t' || c2 == ' ' || c2 == '\n') { ungetsc(c2); break; } *str++ = c2; } while (str < &match[sizeof(match)-1]); *str = '\0'; if (get) { int h = findhistrel(match); if (h >= 0) replace = &history[h]; } else { int h = findhist(-1, 0, match, true); if (h >= 0) replace = &history[h]; } } /* * XXX ksh history buffer saves un-expanded * commands. Until the history buffer code is * changed to contain expanded commands, we * ignore the bad commands (spinning sucks) */ if (replace && **replace == '!') ungetsc(c2); else if (replace) { Source *s; /* do not strdup replacement via alloc */ s = pushs(SREREAD, source->areap); s->start = s->str = *replace; s->next = source; source = s; continue; } else ungetsc(c2); } if (c == '[' && (cf & (VARASN|ARRAYVAR))) { *wp = EOS; /* temporary */ if (is_wdvarname(Xstring(ws, wp), false)) { char *p, *tmp; if (arraysub(&tmp)) { *wp++ = CHAR; *wp++ = c; for (p = tmp; *p; ) { Xcheck(ws, wp); *wp++ = CHAR; *wp++ = *p++; } afree(tmp, ATEMP); break; } else { Source *s; s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = tmp; s->next = source; source = s; } } *wp++ = CHAR; *wp++ = c; break; } /* fall through.. */ Sbase1: /* includes *(...|...) pattern (*+?@!) */ if (c == '*' || c == '@' || c == '+' || c == '?' || c == '!') { c2 = getsc(); if (c2 == '(' /*)*/ ) { *wp++ = OPAT; *wp++ = c; PUSH_STATE(SPATTERN); break; } ungetsc(c2); } /* fall through.. */ Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ switch (c) { case '\\': c = getsc(); if (c) /* trailing \ is lost */ *wp++ = QCHAR, *wp++ = c; break; case '\'': *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SSQUOTE); break; case '"': *wp++ = OQUOTE; PUSH_STATE(SDQUOTE); break; default: goto Subst; } break; Subst: switch (c) { case '\\': c = getsc(); switch (c) { case '"': case '\\': case '$': case '`': *wp++ = QCHAR, *wp++ = c; break; default: Xcheck(ws, wp); if (c) { /* trailing \ is lost */ *wp++ = CHAR, *wp++ = '\\'; *wp++ = CHAR, *wp++ = c; } break; } break; case '$': c = getsc(); if (c == '(') /*)*/ { c = getsc(); if (c == '(') /*)*/ { PUSH_STATE(SASPAREN); statep->ls_sasparen.nparen = 2; statep->ls_sasparen.start = Xsavepos(ws, wp); *wp++ = EXPRSUB; } else { ungetsc(c); PUSH_STATE(SCSPAREN); statep->ls_scsparen.nparen = 1; statep->ls_scsparen.csstate = 0; *wp++ = COMSUB; } } else if (c == '{') /*}*/ { *wp++ = OSUBST; *wp++ = '{'; /*}*/ wp = get_brace_var(&ws, wp); c = getsc(); /* allow :# and :% (ksh88 compat) */ if (c == ':') { *wp++ = CHAR, *wp++ = c; c = getsc(); } /* If this is a trim operation, * treat (,|,) specially in STBRACE. */ if (c == '#' || c == '%') { ungetsc(c); PUSH_STATE(STBRACE); } else { ungetsc(c); PUSH_STATE(SBRACE); } } else if (ctype(c, C_ALPHA)) { *wp++ = OSUBST; *wp++ = 'X'; do { Xcheck(ws, wp); *wp++ = c; c = getsc(); } while (ctype(c, C_ALPHA|C_DIGIT)); *wp++ = '\0'; *wp++ = CSUBST; *wp++ = 'X'; ungetsc(c); } else if (ctype(c, C_DIGIT|C_VAR1)) { Xcheck(ws, wp); *wp++ = OSUBST; *wp++ = 'X'; *wp++ = c; *wp++ = '\0'; *wp++ = CSUBST; *wp++ = 'X'; } else { *wp++ = CHAR, *wp++ = '$'; ungetsc(c); } break; case '`': PUSH_STATE(SBQUOTE); *wp++ = COMSUB; /* Need to know if we are inside double quotes * since sh/at&t-ksh translate the \" to " in * "`..\"..`". * This is not done in posix mode (section * 3.2.3, Double Quotes: "The backquote shall * retain its special meaning introducing the * other form of command substitution (see * 3.6.3). The portion of the quoted string * from the initial backquote and the * characters up to the next backquote that * is not preceded by a backslash (having * escape characters removed) defines that * command whose output replaces `...` when * the word is expanded." * Section 3.6.3, Command Substitution: * "Within the backquoted style of command * substitution, backslash shall retain its * literal meaning, except when followed by * $ ` \."). */ statep->ls_sbquote.indquotes = 0; if (!Flag(FPOSIX)) { Lex_state *s = statep; Lex_state *base = state_info.base; while (1) { for (; s != base; s--) { if (s->ls_state == SDQUOTE) { statep->ls_sbquote.indquotes = 1; break; } } if (s != base) break; if (!(s = s->ls_info.base)) break; base = s-- - STATE_BSIZE; } } break; default: *wp++ = CHAR, *wp++ = c; } break; case SSQUOTE: if (c == '\'') { POP_STATE(); *wp++ = CQUOTE; ignore_backslash_newline--; } else *wp++ = QCHAR, *wp++ = c; break; case SDQUOTE: if (c == '"') { POP_STATE(); *wp++ = CQUOTE; } else goto Subst; break; case SCSPAREN: /* $( .. ) */ /* todo: deal with $(...) quoting properly * kludge to partly fake quoting inside $(..): doesn't * really work because nested $(..) or ${..} inside * double quotes aren't dealt with. */ switch (statep->ls_scsparen.csstate) { case 0: /* normal */ switch (c) { case '(': statep->ls_scsparen.nparen++; break; case ')': statep->ls_scsparen.nparen--; break; case '\\': statep->ls_scsparen.csstate = 1; break; case '"': statep->ls_scsparen.csstate = 2; break; case '\'': statep->ls_scsparen.csstate = 4; ignore_backslash_newline++; break; } break; case 1: /* backslash in normal mode */ case 3: /* backslash in double quotes */ --statep->ls_scsparen.csstate; break; case 2: /* double quotes */ if (c == '"') statep->ls_scsparen.csstate = 0; else if (c == '\\') statep->ls_scsparen.csstate = 3; break; case 4: /* single quotes */ if (c == '\'') { statep->ls_scsparen.csstate = 0; ignore_backslash_newline--; } break; } if (statep->ls_scsparen.nparen == 0) { POP_STATE(); *wp++ = 0; /* end of COMSUB */ } else *wp++ = c; break; case SASPAREN: /* $(( .. )) */ /* todo: deal with $((...); (...)) properly */ /* XXX should nest using existing state machine * (embed "..", $(...), etc.) */ if (c == '(') statep->ls_sasparen.nparen++; else if (c == ')') { statep->ls_sasparen.nparen--; if (statep->ls_sasparen.nparen == 1) { /*(*/ if ((c2 = getsc()) == ')') { POP_STATE(); *wp++ = 0; /* end of EXPRSUB */ break; } else { char *s; ungetsc(c2); /* mismatched parenthesis - * assume we were really * parsing a $(..) expression */ s = Xrestpos(ws, wp, statep->ls_sasparen.start); memmove(s + 1, s, wp - s); *s++ = COMSUB; *s = '('; /*)*/ wp++; statep->ls_scsparen.nparen = 1; statep->ls_scsparen.csstate = 0; state = statep->ls_state = SCSPAREN; } } } *wp++ = c; break; case SBRACE: /*{*/ if (c == '}') { POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; } else goto Sbase1; break; case STBRACE: /* Same as SBRACE, except (,|,) treated specially */ /*{*/ if (c == '}') { POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; } else if (c == '|') { *wp++ = SPAT; } else if (c == '(') { *wp++ = OPAT; *wp++ = ' '; /* simile for @ */ PUSH_STATE(SPATTERN); } else goto Sbase1; break; case SBQUOTE: if (c == '`') { *wp++ = 0; POP_STATE(); } else if (c == '\\') { switch (c = getsc()) { case '\\': case '$': case '`': *wp++ = c; break; case '"': if (statep->ls_sbquote.indquotes) { *wp++ = c; break; } /* fall through.. */ default: if (c) { /* trailing \ is lost */ *wp++ = '\\'; *wp++ = c; } break; } } else *wp++ = c; break; case SWORD: /* ONEWORD */ goto Subst; case SLETPAREN: /* LETEXPR: (( ... )) */ /*(*/ if (c == ')') { if (statep->ls_sletparen.nparen > 0) --statep->ls_sletparen.nparen; /*(*/ else if ((c2 = getsc()) == ')') { c = 0; *wp++ = CQUOTE; goto Done; } else ungetsc(c2); } else if (c == '(') /* parenthesis inside quotes and backslashes * are lost, but at&t ksh doesn't count them * either */ ++statep->ls_sletparen.nparen; goto Sbase2; case SHEREDELIM: /* <<,<<- delimiter */ /* XXX chuck this state (and the next) - use * the existing states ($ and \`..` should be * stripped of their specialness after the * fact). */ /* here delimiters need a special case since * $ and `..` are not to be treated specially */ if (c == '\\') { c = getsc(); if (c) { /* trailing \ is lost */ *wp++ = QCHAR; *wp++ = c; } } else if (c == '\'') { PUSH_STATE(SSQUOTE); *wp++ = OQUOTE; ignore_backslash_newline++; } else if (c == '"') { state = statep->ls_state = SHEREDQUOTE; *wp++ = OQUOTE; } else { *wp++ = CHAR; *wp++ = c; } break; case SHEREDQUOTE: /* " in <<,<<- delimiter */ if (c == '"') { *wp++ = CQUOTE; state = statep->ls_state = SHEREDELIM; } else { if (c == '\\') { switch (c = getsc()) { case '\\': case '"': case '$': case '`': break; default: if (c) { /* trailing \ lost */ *wp++ = CHAR; *wp++ = '\\'; } break; } } *wp++ = CHAR; *wp++ = c; } break; case SPATTERN: /* in *(...|...) pattern (*+?@!) */ if ( /*(*/ c == ')') { *wp++ = CPAT; POP_STATE(); } else if (c == '|') { *wp++ = SPAT; } else if (c == '(') { *wp++ = OPAT; *wp++ = ' '; /* simile for @ */ PUSH_STATE(SPATTERN); } else goto Sbase1; break; } } Done: Xcheck(ws, wp); if (statep != &states[1]) /* XXX figure out what is missing */ yyerror("no closing quote\n"); /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ if (state == SHEREDELIM) state = SBASE; dp = Xstring(ws, wp); if ((c == '<' || c == '>') && state == SBASE && ((c2 = Xlength(ws, wp)) == 0 || (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { struct ioword *iop = (struct ioword *) alloc(sizeof(*iop), ATEMP); if (c2 == 2) iop->unit = dp[1] - '0'; else iop->unit = c == '>'; /* 0 for <, 1 for > */ c2 = getsc(); /* <<, >>, <> are ok, >< is not */ if (c == c2 || (c == '<' && c2 == '>')) { iop->flag = c == c2 ? (c == '>' ? IOCAT : IOHERE) : IORDWR; if (iop->flag == IOHERE) { if ((c2 = getsc()) == '-') iop->flag |= IOSKIP; else ungetsc(c2); } } else if (c2 == '&') iop->flag = IODUP | (c == '<' ? IORDUP : 0); else { iop->flag = c == '>' ? IOWRITE : IOREAD; if (c == '>' && c2 == '|') iop->flag |= IOCLOB; else ungetsc(c2); } iop->name = (char *) 0; iop->delim = (char *) 0; iop->heredoc = (char *) 0; Xfree(ws, wp); /* free word */ yylval.iop = iop; return REDIR; } if (wp == dp && state == SBASE) { Xfree(ws, wp); /* free word */ /* no word, process LEX1 character */ switch (c) { default: return c; case '|': case '&': case ';': if ((c2 = getsc()) == c) c = (c == ';') ? BREAK : (c == '|') ? LOGOR : (c == '&') ? LOGAND : YYERRCODE; else if (c == '|' && c2 == '&') c = COPROC; else ungetsc(c2); return c; case '\n': gethere(); if (cf & CONTIN) goto Again; return c; case '(': /*)*/ if (!Flag(FSH)) { if ((c2 = getsc()) == '(') /*)*/ /* XXX need to handle ((...); (...)) */ c = MDPAREN; else ungetsc(c2); } return c; /*(*/ case ')': return c; } } *wp++ = EOS; /* terminate word */ yylval.cp = Xclose(ws, wp); if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ return LWORD; ungetsc(c); /* unget terminator */ /* copy word to unprefixed string ident */ for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) *dp++ = *sp++; /* Make sure the ident array stays '\0' padded */ memset(dp, 0, (ident+IDENT) - dp + 1); if (c != EOS) *ident = '\0'; /* word is not unquoted */ if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { struct tbl *p; int h = hash(ident); /* { */ if ((cf & KEYWORD) && (p = tsearch(&keywords, ident, h)) && (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { afree(yylval.cp, ATEMP); return p->val.i; } if ((cf & ALIAS) && (p = tsearch(&aliases, ident, h)) && (p->flag & ISSET)) { Source *s; for (s = source; s->type == SALIAS; s = s->next) if (s->u.tblp == p) return LWORD; /* push alias expansion */ s = pushs(SALIAS, source->areap); s->start = s->str = p->val.s; s->u.tblp = p; s->next = source; source = s; afree(yylval.cp, ATEMP); goto Again; } } return LWORD; }
char * do_realpath(const char *upath) { char *xp, *ip, *tp, *ipath, *ldest = NULL; XString xs; size_t pos, len; int llen; struct stat sb; #ifdef MKSH__NO_PATH_MAX size_t ldestlen = 0; #define pathlen sb.st_size #define pathcnd (ldestlen < (pathlen + 1)) #else #define pathlen PATH_MAX #define pathcnd (!ldest) #endif /* max. recursion depth */ int symlinks = 32; if (mksh_abspath(upath)) { /* upath is an absolute pathname */ strdupx(ipath, upath, ATEMP); } else { /* upath is a relative pathname, prepend cwd */ if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp)) return (NULL); ipath = shf_smprintf("%s%s%s", tp, "/", upath); afree(tp, ATEMP); } /* ipath and upath are in memory at the same time -> unchecked */ Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP); /* now jump into the deep of the loop */ goto beginning_of_a_pathname; while (*ip) { /* skip slashes in input */ while (*ip == '/') ++ip; if (!*ip) break; /* get next pathname component from input */ tp = ip; while (*ip && *ip != '/') ++ip; len = ip - tp; /* check input for "." and ".." */ if (tp[0] == '.') { if (len == 1) /* just continue with the next one */ continue; else if (len == 2 && tp[1] == '.') { /* strip off last pathname component */ while (xp > Xstring(xs, xp)) if (*--xp == '/') break; /* then continue with the next one */ continue; } } /* store output position away, then append slash to output */ pos = Xsavepos(xs, xp); /* 1 for the '/' and len + 1 for tp and the NUL from below */ XcheckN(xs, xp, 1 + len + 1); Xput(xs, xp, '/'); /* append next pathname component to output */ memcpy(xp, tp, len); xp += len; *xp = '\0'; /* lstat the current output, see if it's a symlink */ if (mksh_lstat(Xstring(xs, xp), &sb)) { /* lstat failed */ if (errno == ENOENT) { /* because the pathname does not exist */ while (*ip == '/') /* skip any trailing slashes */ ++ip; /* no more components left? */ if (!*ip) /* we can still return successfully */ break; /* more components left? fall through */ } /* not ENOENT or not at the end of ipath */ goto notfound; } /* check if we encountered a symlink? */ if (S_ISLNK(sb.st_mode)) { #ifndef MKSH__NO_SYMLINK /* reached maximum recursion depth? */ if (!symlinks--) { /* yep, prevent infinite loops */ errno = ELOOP; goto notfound; } /* get symlink(7) target */ if (pathcnd) { #ifdef MKSH__NO_PATH_MAX if (notoktoadd(pathlen, 1)) { errno = ENAMETOOLONG; goto notfound; } #endif ldest = aresize(ldest, pathlen + 1, ATEMP); } llen = readlink(Xstring(xs, xp), ldest, pathlen); if (llen < 0) /* oops... */ goto notfound; ldest[llen] = '\0'; /* * restart if symlink target is an absolute path, * otherwise continue with currently resolved prefix */ /* append rest of current input path to link target */ tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip); afree(ipath, ATEMP); ip = ipath = tp; if (!mksh_abspath(ldest)) { /* symlink target is a relative path */ xp = Xrestpos(xs, xp, pos); } else #endif { /* symlink target is an absolute path */ xp = Xstring(xs, xp); beginning_of_a_pathname: /* assert: (ip == ipath)[0] == '/' */ /* assert: xp == xs.beg => start of path */ /* exactly two leading slashes? (SUSv4 3.266) */ if (ip[1] == '/' && ip[2] != '/') { /* keep them, e.g. for UNC pathnames */ Xput(xs, xp, '/'); } } } /* otherwise (no symlink) merely go on */ } /* * either found the target and successfully resolved it, * or found its parent directory and may create it */ if (Xlength(xs, xp) == 0) /* * if the resolved pathname is "", make it "/", * otherwise do not add a trailing slash */ Xput(xs, xp, '/'); Xput(xs, xp, '\0'); /* * if source path had a trailing slash, check if target path * is not a non-directory existing file */ if (ip > ipath && ip[-1] == '/') { if (stat(Xstring(xs, xp), &sb)) { if (errno != ENOENT) goto notfound; } else if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; goto notfound; } /* target now either does not exist or is a directory */ } /* return target path */ if (ldest != NULL) afree(ldest, ATEMP); afree(ipath, ATEMP); return (Xclose(xs, xp)); notfound: /* save; freeing memory might trash it */ llen = errno; if (ldest != NULL) afree(ldest, ATEMP); afree(ipath, ATEMP); Xfree(xs, xp); errno = llen; return (NULL); #undef pathlen #undef pathcnd }
static void readhere(struct ioword *iop) { int c; const char *eof, *eofp; XString xs; char *xp; size_t xpos; eof = evalstr(iop->delim, 0); if (!(iop->ioflag & IOEVAL)) ignore_backslash_newline++; Xinit(xs, xp, 256, ATEMP); heredoc_read_line: /* beginning of line */ eofp = eof; xpos = Xsavepos(xs, xp); if (iop->ioflag & IOSKIP) { /* skip over leading tabs */ while ((c = getsc()) == '\t') ; /* nothing */ goto heredoc_parse_char; } heredoc_read_char: c = getsc(); heredoc_parse_char: /* compare with here document marker */ if (!*eofp) { /* end of here document marker, what to do? */ switch (c) { case /*(*/ ')': if (!subshell_nesting_type) /*- * not allowed outside $(...) or (...) * => mismatch */ break; /* allow $(...) or (...) to close here */ ungetsc(/*(*/ ')'); /* FALLTHROUGH */ case 0: /* * Allow EOF here to commands without trailing * newlines (mksh -c '...') will work as well. */ case '\n': /* Newline terminates here document marker */ goto heredoc_found_terminator; } } else if (c == *eofp++) /* store; then read and compare next character */ goto heredoc_store_and_loop; /* nope, mismatch; read until end of line */ while (c != '\n') { if (!c) /* oops, reached EOF */ yyerror("%s '%s' unclosed\n", "here document", eof); /* store character */ Xcheck(xs, xp); Xput(xs, xp, c); /* read next character */ c = getsc(); } /* we read a newline as last character */ heredoc_store_and_loop: /* store character */ Xcheck(xs, xp); Xput(xs, xp, c); if (c == '\n') goto heredoc_read_line; goto heredoc_read_char; heredoc_found_terminator: /* jump back to saved beginning of line */ xp = Xrestpos(xs, xp, xpos); /* terminate, close and store */ Xput(xs, xp, '\0'); iop->heredoc = Xclose(xs, xp); if (!(iop->ioflag & IOEVAL)) ignore_backslash_newline--; }
void echo_stream() { int acceptor, sock; if (signal(SIGCHLD, reaper) == SIG_ERR) { die(-1, "unable to catch SIGCHLD"); } say("Stream service started\n"); /* Prepare XSSL context and get SID (based on RSA key) */ XSSL_CTX *ctx = XSSL_CTX_new(); if (ctx == NULL) { die(-5, "Unable to create new XSSL_CTX\n"); } char *SID = SID_from_keypair(ctx->keypair); /* Prepare listen socket, binding to generated SID */ if ((acceptor = Xsocket(AF_XIA, SOCK_STREAM, 0)) < 0) die(-2, "unable to create the stream socket\n"); struct addrinfo *ai; if (Xgetaddrinfo(NULL, SID, NULL, &ai) != 0) die(-1, "getaddrinfo failure!\n"); Graph g((sockaddr_x*)ai->ai_addr); sockaddr_x *sa = (sockaddr_x*)ai->ai_addr; printf("\nStream DAG\n%s\n", g.dag_string().c_str()); if (XregisterName(STREAM_NAME, sa) < 0 ) die(-1, "error registering name: %s\n", STREAM_NAME); say("registered name: \n%s\n", STREAM_NAME); if (Xbind(acceptor, (struct sockaddr *)sa, sizeof(sockaddr_x)) < 0) { die(-3, "unable to bind to the dag\n"); } Xlisten(acceptor, 5); while (1) { say("Xsock %4d waiting for a new connection.\n", acceptor); if ((sock = Xaccept(acceptor, NULL, 0)) < 0) { warn("Xsock %d accept failure! error = %d\n", acceptor, errno); // FIXME: should we die here instead or try and recover? continue; } say ("Xsock %4d new session\n", sock); pid_t pid = fork(); if (pid == -1) { die(-1, "FORK FAILED\n"); } else if (pid == 0) { process(sock, ctx); exit(0); } else { // FIXME: we need to close the socket in the main process or the file // descriptor limit will be hit. But if Xclose is called, the connection // is torn down in click as well keeping the child process from using it. // for now use a regular close to shut it here without affecting the child. close(sock); } } Xclose(acceptor); XSSL_CTX_free(ctx); free(SID); }
/* ** get data from the client and return it ** run in a child process that was forked off from the main process. */ void process(int sock, XSSL_CTX *ctx) { char buf[XIA_MAXBUF + 1]; int n; pid_t pid = getpid(); fd_set fds; FD_ZERO(&fds); FD_SET(sock, &fds); struct timeval tv; tv.tv_sec = WAIT_FOR_DATA; tv.tv_usec = 0; /* set up XSSL connection */ XSSL *xssl = XSSL_new(ctx); if (xssl == NULL) { die(-6, "Error creating new XSSL object\n"); } if (XSSL_set_fd(xssl, sock) != 1) { die(-7, "Error setting XSSL sockfd\n"); } if (XSSL_accept(xssl) != 1) { die(-8, "Error accepting XSSL connection\n"); } while (1) { memset(buf, 0, sizeof(buf)); tv.tv_sec = WAIT_FOR_DATA; tv.tv_usec = 0; if ((n = Xselect(xssl->sockfd + 1, &fds, NULL, NULL, &tv)) < 0) { warn("%5d Select failed, closing...\n", pid); break; } else if (n == 0) { // we timed out, close the socket say("%5d timed out on recv\n", pid); break; } else if (!FD_ISSET(xssl->sockfd, &fds)) { // this shouldn't happen! die(-4, "something is really wrong, exiting\n"); } if ((n = XSSL_read(xssl, buf, sizeof(buf))) < 0) { warn("Recv error on socket %d, closing connection\n", pid); break; } say("%5d received %d bytes\n", pid, n); if ((n = XSSL_write(xssl, buf, n)) < 0) { warn("%5d send error\n", pid); break; } say("%5d sent %d bytes\n", pid, n); } say("%5d closing\n", pid); XSSL_shutdown(xssl); Xclose(sock); XSSL_free(xssl); }