/*@Header@*/ /* $Id: table.c,v 1.7 2012/11/12 20:10:02 ksb Exp $ * wire marshall code for wrapw and sshw -- ksb */ #if TABEL_TESTING #include #include #include #include #include #include #endif static char acTableId[] = "$Id: table.c,v 1.7 2012/11/12 20:10:02 ksb Exp $"; /*@Explode read@*/ /* Read \nstringZ\000\n, return ps list (ksb) * Convention is put a dot ('.') in for a NULL table, which is not * a '\n' or a '\000' for the first if-statement below. That way you * could type a environment from the keyboard, if you were nuts. */ static char * ReadTable(int iFd, char *pcSize/* 128 bytes at least */) { register size_t wAllow; register int i, iCc; register char *pcMem; auto char *pcOut; auto char acCount[128]; /* "%d" */ if ((char *)0 == pcSize) { pcSize = acCount; } wAllow = 0; for (i = 0; i < sizeof(acCount) && 1 == read(iFd, pcSize+i, 1); ++i) { if (!isdigit(pcSize[i])) break; } if (0 == i || ('\n' != pcSize[i] && '\000' != pcSize[i])) { return (char *)0; } pcSize[i] = '\000'; wAllow = strtol(pcSize, &pcOut, 0); /* Not a number we can parse? Sigh. Might be too big? */ if (pcOut == pcSize) { return (char *)0; } ++wAllow; /* for the \n (or \000) on the end */ if ((char *)0 == (pcMem = malloc((wAllow|7)+1))) { return (char *)0; } for (i = 0; 0 < wAllow && 0 < (iCc = read(iFd, pcMem+i, wAllow)); wAllow -= iCc, i += iCc) { /* we just got the next chunk */ } --i; if ('\n' != pcMem[i] && '\000' != pcMem[i]) { return (char *)0; } pcMem[i] = '\000'; return pcMem; } /*@Explode send@*/ static char acNoData[] = "00\n\000"; /* The max number of writes to group into a writev, 5 to 55 is usually fine. */ #if !defined TABLE_CHUNKS #define TABLE_CHUNKS 16 /* inv. > 1 */ #endif /* Send an argv (string table) to the client with an allocation header (ksb) * accepts a allocation size, the data, \n (like ReadBlock) */ static int SendTable(int iFd, const char **ppcTable) { register size_t wAllow; register int i, iChunk; register const char *pcScan; auto struct iovec aIV[TABLE_CHUNKS]; auto char acCount[128]; /* "%d" */ static const char acEOT[] = "\n"; if ((const char **)0 == ppcTable || (char *)0 == *ppcTable) { return write(iFd, acNoData, sizeof(acNoData)-1); } wAllow = 0; for (i = 0; (char *)0 != (pcScan = ppcTable[i]); ++i) { wAllow += strlen(pcScan)+1; } snprintf(acCount, sizeof(acCount), "%ld\n", (long)wAllow); iChunk = 0; aIV[iChunk].iov_base = acCount; aIV[iChunk++].iov_len = strlen(acCount); for (i = 0; ; ++i) { if ((char *)0 == (pcScan = ppcTable[i])) pcScan = acEOT; if (iChunk < TABLE_CHUNKS) /* nada */; else if (-1 == writev(iFd, aIV, iChunk)) return -1; else iChunk = 0; aIV[iChunk].iov_base = (char *)pcScan; aIV[iChunk++].iov_len = strlen(pcScan)+(pcScan != acEOT); if (acEOT == pcScan) break; } return writev(iFd, aIV, iChunk); } /* Send a C string to the client, with an allocation header (ksb) * This mocks the table code (above), so clients might read the reply * from either with the same code. */ static int SendString(int iFd, const char *pcData) { register size_t wAllow; auto char acCount[128]; /* "%d" */ auto struct iovec aIV[4]; if ((const char *)0 == pcData) { return write(iFd, acNoData, sizeof(acNoData)-1); } wAllow = strlen(pcData)+1; snprintf(acCount, sizeof(acCount), "%ld\n", (long)wAllow); aIV[0].iov_base = acCount; aIV[0].iov_len = strlen(acCount); aIV[1].iov_base = (char *)pcData; aIV[1].iov_len = wAllow; aIV[2].iov_base = "\n"; /* cheap sync mark */ aIV[2].iov_len = 1; return writev(iFd, aIV, 3); } /*@Remove@*/ #if TABLE_REPEAT /*@Explode repeat@*/ /* Send the same format we read (a cat of non-empty strings). (ksb) * Used for a proxy service that reads a table just to forward it. */ static int SendRepeat(int iFd, const char *psData, const char *pcSize) { register size_t w, wLen; register const char *pc; auto struct iovec aIV[4]; auto char acCount[128]; /* %ld\n */ if ((char *)0 != pcSize) { wLen = atoi(pcSize); } else { for (w = 0, pc = psData; '\000' != *pc; w += wLen, pc += wLen) wLen = strlen(pc)+1; snprintf(acCount, sizeof(acCount), "%ld\n", (long)wLen); pcSize = acCount; } aIV[0].iov_base = pcSize; aIV[0].iov_len = strlen(pcSize)+1; aIV[1].iov_base = (char *)psData; aIV[1].iov_len = wLen; aIV[2].iov_base = "\n"; /* cheap sync mark */ aIV[2].iov_len = 1; return writev(iFd, aIV, 3); } /*@Remove@*/ #endif #if TABLE_TOVECTOR /*@Explode tovector@*/ /* Convert a raw sting table (string\000string2\000...\000\000) (ksb) * into an argv vector (char *)'s with sentinal NULL at the end. * If we know the size of the table we can take empty strings. */ static const char ** TableToVector(const char *psTable, const char *pcSize) { register const char **ppcMem, *pcScan; register size_t wCount, wFound; wCount = 0; if ((char *)0 != pcSize) { register size_t wLen = atoi(pcSize); pcScan = psTable; while (wLen > 0) { register size_t w; w = strlen(pcScan)+1; pcScan += w; wLen -= w; ++wCount; } } else { for (pcScan = psTable; '\000' != *pcScan; pcScan += strlen(pcScan)+1) ++wCount; } if ((const char **)0 == (ppcMem = calloc((wCount|1)+1, sizeof(char *)))) { return (const char **)0; } for (wFound = 0, pcScan = psTable; wFound < wCount; pcScan += strlen(pcScan)+1) ppcMem[wFound++] = pcScan; ppcMem[wFound] = (char *)0; return ppcMem; } /*@Remove@*/ #endif