1/* 2 * zselect.c - builtin support for select system call 3 * 4 * This file is part of zsh, the Z shell. 5 * 6 * Copyright (c) 1998-2001 Peter Stephenson 7 * All rights reserved. 8 * 9 * Permission is hereby granted, without written agreement and without 10 * license or royalty fees, to use, copy, modify, and distribute this 11 * software and to distribute modified versions of this software for any 12 * purpose, provided that the above copyright notice and the following 13 * two paragraphs appear in all copies of this software. 14 * 15 * In no event shall Peter Stephenson or the Zsh Development 16 * Group be liable to any party for direct, indirect, special, incidental, 17 * or consequential damages arising out of the use of this software and 18 * its documentation, even if Peter Stephenson, and the Zsh 19 * Development Group have been advised of the possibility of such damage. 20 * 21 * Peter Stephenson and the Zsh Development Group specifically 22 * disclaim any warranties, including, but not limited to, the implied 23 * warranties of merchantability and fitness for a particular purpose. The 24 * software provided hereunder is on an "as is" basis, and Peter Stephenson 25 * and the Zsh Development Group have no obligation to provide maintenance, 26 * support, updates, enhancements, or modifications. 27 * 28 */ 29 30#include "zselect.mdh" 31#include "zselect.pro" 32 33/* Helper functions */ 34 35/* 36 * Handle an fd by adding it to the current fd_set. 37 * Return 1 for error (after printing a message), 0 for OK. 38 */ 39static int 40handle_digits(char *nam, char *argptr, fd_set *fdset, int *fdmax) 41{ 42 int fd; 43 char *endptr; 44 45 if (!idigit(*argptr)) { 46 zwarnnam(nam, "expecting file descriptor: %s", argptr); 47 return 1; 48 } 49 fd = (int)zstrtol(argptr, &endptr, 10); 50 if (*endptr) { 51 zwarnnam(nam, "garbage after file descriptor: %s", endptr); 52 return 1; 53 } 54 55 FD_SET(fd, fdset); 56 if (fd+1 > *fdmax) 57 *fdmax = fd+1; 58 return 0; 59} 60 61/* The builtin itself */ 62 63/**/ 64static int 65bin_zselect(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) 66{ 67#ifdef HAVE_SELECT 68 int i, fd, fdsetind = 0, fdmax = 0, fdcount; 69 fd_set fdset[3]; 70 const char fdchar[3] = "rwe"; 71 struct timeval tv, *tvptr = NULL; 72 char *outarray = "reply", **outdata, **outptr; 73 char *outhash = NULL; 74 LinkList fdlist; 75 76 for (i = 0; i < 3; i++) 77 FD_ZERO(fdset+i); 78 79 for (; *args; args++) { 80 char *argptr = *args, *endptr; 81 zlong tempnum; 82 if (*argptr == '-') { 83 for (argptr++; *argptr; argptr++) { 84 switch (*argptr) { 85 /* 86 * Array name for reply, if not $reply. 87 * This gets set to e.g. `-r 0 -w 1' if 0 is ready 88 * for reading and 1 is ready for writing. 89 */ 90 case 'a': 91 case 'A': 92 i = *argptr; 93 if (argptr[1]) 94 argptr++; 95 else if (args[1]) { 96 argptr = *++args; 97 } else { 98 zwarnnam(nam, "argument expected after -%c", *argptr); 99 return 1; 100 } 101 if (idigit(*argptr) || !isident(argptr)) { 102 zwarnnam(nam, "invalid array name: %s", argptr); 103 return 1; 104 } 105 if (i == 'a') 106 outarray = argptr; 107 else 108 outhash = argptr; 109 /* set argptr to next to last char because of increment */ 110 while (argptr[1]) 111 argptr++; 112 break; 113 114 /* Following numbers indicate fd's for reading */ 115 case 'r': 116 fdsetind = 0; 117 break; 118 119 /* Following numbers indicate fd's for writing */ 120 case 'w': 121 fdsetind = 1; 122 break; 123 124 /* Following numbers indicate fd's for errors */ 125 case 'e': 126 fdsetind = 2; 127 break; 128 129 /* 130 * Get a timeout value in hundredths of a second 131 * (same units as KEYTIMEOUT). 0 means just poll. 132 * If not given, blocks indefinitely. 133 */ 134 case 't': 135 if (argptr[1]) 136 argptr++; 137 else if (args[1]) { 138 argptr = *++args; 139 } else { 140 zwarnnam(nam, "argument expected after -%c", *argptr); 141 return 1; 142 } 143 if (!idigit(*argptr)) { 144 zwarnnam(nam, "number expected after -t"); 145 return 1; 146 } 147 tempnum = zstrtol(argptr, &endptr, 10); 148 if (*endptr) { 149 zwarnnam(nam, "garbage after -t argument: %s", 150 endptr); 151 return 1; 152 } 153 /* timevalue now active */ 154 tvptr = &tv; 155 tv.tv_sec = (long)(tempnum / 100); 156 tv.tv_usec = (long)(tempnum % 100) * 10000L; 157 158 /* remember argptr is incremented at end of loop */ 159 argptr = endptr - 1; 160 break; 161 162 /* Digits following option without arguments are fd's. */ 163 default: 164 if (handle_digits(nam, argptr, fdset+fdsetind, 165 &fdmax)) 166 return 1; 167 } 168 } 169 } else if (handle_digits(nam, argptr, fdset+fdsetind, &fdmax)) 170 return 1; 171 } 172 173 errno = 0; 174 do { 175 i = select(fdmax, (SELECT_ARG_2_T)fdset, (SELECT_ARG_2_T)(fdset+1), 176 (SELECT_ARG_2_T)(fdset+2), tvptr); 177 } while (i < 0 && errno == EINTR && !errflag); 178 179 if (i <= 0) { 180 if (i < 0) 181 zwarnnam(nam, "error on select: %e", errno); 182 /* else no fd's set. Presumably a timeout. */ 183 return 1; 184 } 185 186 /* 187 * Make a linked list of all file descriptors which are ready. 188 * These go into an array preceded by -r, -w or -e for read, write, 189 * error as appropriate. Typically there will only be one set 190 * so this looks rather like overkill. 191 */ 192 fdlist = znewlinklist(); 193 for (i = 0; i < 3; i++) { 194 int doneit = 0; 195 for (fd = 0; fd < fdmax; fd++) { 196 if (FD_ISSET(fd, fdset+i)) { 197 char buf[BDIGBUFSIZE]; 198 if (outhash) { 199 /* 200 * Key/value pairs; keys are fd's (as strings), 201 * value is a (possibly improper) subset of "rwe". 202 */ 203 LinkNode nptr; 204 int found = 0; 205 206 convbase(buf, fd, 10); 207 for (nptr = firstnode(fdlist); nptr; 208 nptr = nextnode(nextnode(nptr))) { 209 if (!strcmp((char *)getdata(nptr), buf)) { 210 /* Already there, add new character. */ 211 void **dataptr = getaddrdata(nextnode(nptr)); 212 char *data = (char *)*dataptr, *ptr; 213 found = 1; 214 if (!strchr(data, fdchar[i])) { 215 strcpy(buf, data); 216 for (ptr = buf; *ptr; ptr++) 217 ; 218 *ptr++ = fdchar[i]; 219 *ptr = '\0'; 220 zsfree(data); 221 *dataptr = ztrdup(buf); 222 } 223 break; 224 } 225 } 226 if (!found) { 227 /* Add new key/value pair. */ 228 zaddlinknode(fdlist, ztrdup(buf)); 229 buf[0] = fdchar[i]; 230 buf[1] = '\0'; 231 zaddlinknode(fdlist, ztrdup(buf)); 232 } 233 } else { 234 /* List of fd's preceded by -r, -w, -e. */ 235 if (!doneit) { 236 buf[0] = '-'; 237 buf[1] = fdchar[i]; 238 buf[2] = 0; 239 zaddlinknode(fdlist, ztrdup(buf)); 240 doneit = 1; 241 } 242 convbase(buf, fd, 10); 243 zaddlinknode(fdlist, ztrdup(buf)); 244 } 245 } 246 } 247 } 248 249 /* convert list to array */ 250 fdcount = countlinknodes(fdlist); 251 outptr = outdata = (char **)zalloc((fdcount+1)*sizeof(char *)); 252 while (nonempty(fdlist)) 253 *outptr++ = getlinknode(fdlist); 254 *outptr = '\0'; 255 /* and store in array parameter */ 256 if (outhash) 257 sethparam(outhash, outdata); 258 else 259 setaparam(outarray, outdata); 260 freelinklist(fdlist, NULL); 261 262 return 0; 263#else 264 /* TODO: use poll */ 265 zerrnam(nam, "your system does not implement the select system call."); 266 return 2; 267#endif 268} 269 270 271static struct builtin bintab[] = { 272 BUILTIN("zselect", 0, bin_zselect, 0, -1, 0, NULL, NULL), 273}; 274 275static struct features module_features = { 276 bintab, sizeof(bintab)/sizeof(*bintab), 277 NULL, 0, 278 NULL, 0, 279 NULL, 0, 280 0 281}; 282 283 284/* The load/unload routines required by the zsh library interface */ 285 286/**/ 287int 288setup_(UNUSED(Module m)) 289{ 290 return 0; 291} 292 293/**/ 294int 295features_(Module m, char ***features) 296{ 297 *features = featuresarray(m, &module_features); 298 return 0; 299} 300 301/**/ 302int 303enables_(Module m, int **enables) 304{ 305 return handlefeatures(m, &module_features, enables); 306} 307 308/**/ 309int 310boot_(Module m) 311{ 312 return 0; 313} 314 315 316/**/ 317int 318cleanup_(Module m) 319{ 320 return setfeatureenables(m, &module_features, NULL); 321} 322 323/**/ 324int 325finish_(UNUSED(Module m)) 326{ 327 return 0; 328} 329