|
1 // utils.c - miscellaneous utilities |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * This file is part of zsh, the Z shell. |
|
7 * |
|
8 * Copyright (c) 1992-1997 Paul Falstad |
|
9 * All rights reserved. |
|
10 * |
|
11 * Permission is hereby granted, without written agreement and without |
|
12 * license or royalty fees, to use, copy, modify, and distribute this |
|
13 * software and to distribute modified versions of this software for any |
|
14 * purpose, provided that the above copyright notice and the following |
|
15 * two paragraphs appear in all copies of this software. |
|
16 * |
|
17 * In no event shall Paul Falstad or the Zsh Development Group be liable |
|
18 * to any party for direct, indirect, special, incidental, or consequential |
|
19 * damages arising out of the use of this software and its documentation, |
|
20 * even if Paul Falstad and the Zsh Development Group have been advised of |
|
21 * the possibility of such damage. |
|
22 * |
|
23 * Paul Falstad and the Zsh Development Group specifically disclaim any |
|
24 * warranties, including, but not limited to, the implied warranties of |
|
25 * merchantability and fitness for a particular purpose. The software |
|
26 * provided hereunder is on an "as is" basis, and Paul Falstad and the |
|
27 * Zsh Development Group have no obligation to provide maintenance, |
|
28 * support, updates, enhancements, or modifications. |
|
29 * |
|
30 */ |
|
31 |
|
32 #include "zsh.mdh" |
|
33 #include "utils.pro" |
|
34 |
|
35 #ifdef __SYMBIAN32__ |
|
36 #ifdef __WINSCW__ |
|
37 #pragma warn_possunwant off |
|
38 #endif//__WINSCW__ |
|
39 #endif//__SYMBIAN32__ |
|
40 |
|
41 #ifdef __SYMBIAN32__ |
|
42 #include "dummy.h" |
|
43 #endif //__SYMBIAN32__ |
|
44 |
|
45 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined (__STDC_ISO_10646__) |
|
46 # include <wchar.h> |
|
47 #else |
|
48 # ifdef HAVE_LANGINFO_H |
|
49 # include <langinfo.h> |
|
50 # if defined(HAVE_ICONV_H) || defined(HAVE_ICONV) || defined(HAVE_LIBICONV) |
|
51 # include <iconv.h> |
|
52 # endif |
|
53 # endif |
|
54 #endif |
|
55 |
|
56 /* name of script being sourced */ |
|
57 |
|
58 /**/ |
|
59 char *scriptname; |
|
60 |
|
61 /* Print an error */ |
|
62 |
|
63 /**/ |
|
64 mod_export void |
|
65 zerr(const char *fmt, const char *str, int num) |
|
66 { |
|
67 if (errflag || noerrs) { |
|
68 if (noerrs < 2) |
|
69 errflag = 1; |
|
70 return; |
|
71 } |
|
72 zwarn(fmt, str, num); |
|
73 errflag = 1; |
|
74 } |
|
75 |
|
76 /**/ |
|
77 mod_export void |
|
78 zerrnam(const char *cmd, const char *fmt, const char *str, int num) |
|
79 { |
|
80 if (errflag || noerrs) |
|
81 return; |
|
82 |
|
83 zwarnnam(cmd, fmt, str, num); |
|
84 errflag = 1; |
|
85 } |
|
86 |
|
87 /**/ |
|
88 mod_export void |
|
89 zwarn(const char *fmt, const char *str, int num) |
|
90 { |
|
91 if (errflag || noerrs) |
|
92 return; |
|
93 if (isatty(2)) |
|
94 trashzle(); |
|
95 /* |
|
96 * scriptname is set when sourcing scripts, so that we get the |
|
97 * correct name instead of the generic name of whatever |
|
98 * program/script is running. It's also set in shell functions, |
|
99 * so test locallevel, too. |
|
100 */ |
|
101 #ifdef __SYMBIAN32__ |
|
102 dup2(1, 2); //redirect stdout to stdin. |
|
103 #endif// __SYMBIAN32__ |
|
104 |
|
105 nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" : |
|
106 scriptname ? scriptname : argzero, stderr); |
|
107 fputc((unsigned char)':', stderr); |
|
108 zerrmsg(fmt, str, num); |
|
109 } |
|
110 |
|
111 /**/ |
|
112 mod_export void |
|
113 zwarnnam(const char *cmd, const char *fmt, const char *str, int num) |
|
114 { |
|
115 #ifdef __SYMBIAN32__ |
|
116 dup2(1, 2); //redirect stdout to stdout. |
|
117 #endif// __SYMBIAN32__ |
|
118 if (!cmd) { |
|
119 zwarn(fmt, str, num); |
|
120 return; |
|
121 } |
|
122 if (errflag || noerrs) |
|
123 return; |
|
124 trashzle(); |
|
125 if (unset(SHINSTDIN) || locallevel) { |
|
126 nicezputs(scriptname ? scriptname : argzero, stderr); |
|
127 fputc((unsigned char)':', stderr); |
|
128 } |
|
129 nicezputs(cmd, stderr); |
|
130 fputc((unsigned char)':', stderr); |
|
131 zerrmsg(fmt, str, num); |
|
132 } |
|
133 |
|
134 #ifdef __CYGWIN__ |
|
135 /* |
|
136 * This works around an occasional problem with dllwrap on Cygwin, seen |
|
137 * on at least two installations. It fails to find the last symbol |
|
138 * exported in alphabetical order (in our case zwarnnam). Until this is |
|
139 * properly categorised and fixed we add a dummy symbol at the end. |
|
140 */ |
|
141 mod_export void |
|
142 zz_plural_z_alpha(void) |
|
143 { |
|
144 } |
|
145 #endif |
|
146 |
|
147 /**/ |
|
148 void |
|
149 zerrmsg(const char *fmt, const char *str, int num) |
|
150 { |
|
151 dup2(1, 2); |
|
152 if ((unset(SHINSTDIN) || locallevel) && lineno) |
|
153 fprintf(stderr, "%ld: ", (long)lineno); |
|
154 else |
|
155 fputc((unsigned char)' ', stderr); |
|
156 |
|
157 while (*fmt) |
|
158 if (*fmt == '%') { |
|
159 fmt++; |
|
160 switch (*fmt++) { |
|
161 case 's': |
|
162 nicezputs(str, stderr); |
|
163 break; |
|
164 case 'l': { |
|
165 char *s; |
|
166 num = metalen(str, num); |
|
167 s = zhalloc(num + 1); |
|
168 memcpy(s, str, num); |
|
169 s[num] = '\0'; |
|
170 nicezputs(s, stderr); |
|
171 break; |
|
172 } |
|
173 case 'd': |
|
174 fprintf(stderr, "%d", num); |
|
175 break; |
|
176 case '%': |
|
177 putc('%', stderr); |
|
178 break; |
|
179 case 'c': |
|
180 fputs(nicechar(num), stderr); |
|
181 break; |
|
182 case 'e': |
|
183 /* print the corresponding message for this errno */ |
|
184 if (num == EINTR) { |
|
185 fputs("interrupt\n", stderr); |
|
186 errflag = 1; |
|
187 return; |
|
188 } |
|
189 /* If the message is not about I/O problems, it looks better * |
|
190 * if we uncapitalize the first letter of the message */ |
|
191 if (num == EIO) |
|
192 fputs(strerror(num), stderr); |
|
193 else { |
|
194 char *errmsg = strerror(num); |
|
195 fputc(tulower(errmsg[0]), stderr); |
|
196 fputs(errmsg + 1, stderr); |
|
197 } |
|
198 break; |
|
199 } |
|
200 } else { |
|
201 putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, stderr); |
|
202 fmt++; |
|
203 } |
|
204 putc('\n', stderr); |
|
205 fflush(stderr); |
|
206 } |
|
207 |
|
208 /* Output a single character, for the termcap routines. * |
|
209 * This is used instead of putchar since it can be a macro. */ |
|
210 |
|
211 /**/ |
|
212 mod_export int |
|
213 putraw(int c) |
|
214 { |
|
215 putc(c, stdout); |
|
216 return 0; |
|
217 } |
|
218 |
|
219 /* Output a single character, for the termcap routines. */ |
|
220 |
|
221 /**/ |
|
222 mod_export int |
|
223 putshout(int c) |
|
224 { |
|
225 putc(c, shout); |
|
226 return 0; |
|
227 } |
|
228 |
|
229 /* Turn a character into a visible representation thereof. The visible * |
|
230 * string is put together in a static buffer, and this function returns * |
|
231 * a pointer to it. Printable characters stand for themselves, DEL is * |
|
232 * represented as "^?", newline and tab are represented as "\n" and * |
|
233 * "\t", and normal control characters are represented in "^C" form. * |
|
234 * Characters with bit 7 set, if unprintable, are represented as "\M-" * |
|
235 * followed by the visible representation of the character with bit 7 * |
|
236 * stripped off. Tokens are interpreted, rather than being treated as * |
|
237 * literal characters. */ |
|
238 |
|
239 /**/ |
|
240 mod_export char * |
|
241 nicechar(int c) |
|
242 { |
|
243 static char buf[6]; |
|
244 char *s = buf; |
|
245 c &= 0xff; |
|
246 if (isprint(c)) |
|
247 goto done; |
|
248 if (c & 0x80) { |
|
249 if (isset(PRINTEIGHTBIT)) |
|
250 goto done; |
|
251 *s++ = '\\'; |
|
252 *s++ = 'M'; |
|
253 *s++ = '-'; |
|
254 c &= 0x7f; |
|
255 if(isprint(c)) |
|
256 goto done; |
|
257 } |
|
258 if (c == 0x7f) { |
|
259 *s++ = '^'; |
|
260 c = '?'; |
|
261 } else if (c == '\n') { |
|
262 *s++ = '\\'; |
|
263 c = 'n'; |
|
264 } else if (c == '\t') { |
|
265 *s++ = '\\'; |
|
266 c = 't'; |
|
267 } else if (c < 0x20) { |
|
268 *s++ = '^'; |
|
269 c += 0x40; |
|
270 } |
|
271 done: |
|
272 *s++ = c; |
|
273 *s = 0; |
|
274 return buf; |
|
275 } |
|
276 |
|
277 /* Output a string's visible representation. */ |
|
278 |
|
279 #if 0 /**/ |
|
280 void |
|
281 nicefputs(char *s, FILE *f) |
|
282 { |
|
283 for (; *s; s++) |
|
284 fputs(nicechar(STOUC(*s)), f); |
|
285 } |
|
286 #endif |
|
287 |
|
288 /* Return the length of the visible representation of a string. */ |
|
289 |
|
290 /**/ |
|
291 size_t |
|
292 nicestrlen(char *s) |
|
293 { |
|
294 size_t l = 0; |
|
295 |
|
296 for (; *s; s++) |
|
297 l += strlen(nicechar(STOUC(*s))); |
|
298 return l; |
|
299 } |
|
300 |
|
301 /* get a symlink-free pathname for s relative to PWD */ |
|
302 |
|
303 /**/ |
|
304 #ifdef __SYMBIAN32__ |
|
305 #define SYM_ROOT '\\' |
|
306 char drive; |
|
307 #endif |
|
308 |
|
309 char * |
|
310 findpwd(char *s) |
|
311 { |
|
312 char *t; |
|
313 drive=*s; |
|
314 |
|
315 if (*(s+2) == SYM_ROOT) |
|
316 return xsymlink(s); |
|
317 s = tricat((pwd[1]) ? pwd : "", "/", s); |
|
318 t = xsymlink(s); |
|
319 zsfree(s); |
|
320 return t; |
|
321 } |
|
322 |
|
323 /* Check whether a string contains the * |
|
324 * name of the present directory. */ |
|
325 |
|
326 /**/ |
|
327 int |
|
328 ispwd(char *s) |
|
329 { |
|
330 struct stat sbuf, tbuf; |
|
331 |
|
332 if (stat(unmeta(s), &sbuf) == 0 && stat(".", &tbuf) == 0) |
|
333 if (sbuf.st_dev == tbuf.st_dev && sbuf.st_ino == tbuf.st_ino) |
|
334 return 1; |
|
335 return 0; |
|
336 } |
|
337 |
|
338 static char xbuf[PATH_MAX*2]; |
|
339 |
|
340 /**/ |
|
341 static char ** |
|
342 slashsplit(char *s) |
|
343 { |
|
344 char *t, **r, **q; |
|
345 int t0; |
|
346 |
|
347 if (!*s) |
|
348 return (char **) zshcalloc(sizeof(char **)); |
|
349 |
|
350 for (t = s, t0 = 0; *t; t++) |
|
351 if (*t == '\\') |
|
352 t0++; |
|
353 q = r = (char **) zalloc(sizeof(char **) * (t0 + 2)); |
|
354 |
|
355 while ((t = strchr(s, '\\'))) { |
|
356 *q++ = ztrduppfx(s, t - s); |
|
357 while (*t == '\\') |
|
358 t++; |
|
359 if (!*t) { |
|
360 *q = NULL; |
|
361 return r; |
|
362 } |
|
363 s = t; |
|
364 } |
|
365 *q++ = ztrdup(s); |
|
366 *q = NULL; |
|
367 return r; |
|
368 } |
|
369 |
|
370 /* expands symlinks and .. or . expressions */ |
|
371 /* if flag = 0, only expand .. and . expressions */ |
|
372 |
|
373 /**/ |
|
374 static int |
|
375 xsymlinks(char *s) |
|
376 { |
|
377 char **pp, **opp; |
|
378 char xbuf2[PATH_MAX*2], xbuf3[PATH_MAX*2]; |
|
379 int t0, ret = 0; |
|
380 int init_buf=0; |
|
381 |
|
382 opp = pp = slashsplit(s); |
|
383 for (; *pp; pp++) { |
|
384 if (!strcmp(*pp, ".")) { |
|
385 zsfree(*pp); |
|
386 continue; |
|
387 } |
|
388 if (!strcmp(*pp, "..")) { |
|
389 char *p; |
|
390 |
|
391 zsfree(*pp); |
|
392 if (!strcmp(xbuf, "\\")) |
|
393 continue; |
|
394 p = xbuf + strlen(xbuf); |
|
395 while (*--p != '\\'); |
|
396 *(p+1) = '\0'; |
|
397 continue; |
|
398 } |
|
399 if(!init_buf) |
|
400 sprintf(xbuf2, "%s%c:\\%s",xbuf, drive, *pp); |
|
401 else |
|
402 sprintf(xbuf2, "%s\\%s", xbuf, *pp); |
|
403 |
|
404 t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX); |
|
405 if (t0 == -1) { |
|
406 if(!init_buf) |
|
407 { |
|
408 strcat(xbuf, &drive); |
|
409 strcat(xbuf, ":\\"); |
|
410 } |
|
411 else |
|
412 strcat(xbuf, "\\"); |
|
413 |
|
414 strcat(xbuf, *pp); |
|
415 zsfree(*pp); |
|
416 } else { |
|
417 ret = 1; |
|
418 metafy(xbuf3, t0, META_NOALLOC); |
|
419 if (*xbuf3 == '\\') { |
|
420 strcpy(xbuf, ""); |
|
421 xsymlinks(xbuf3 + 1); |
|
422 } else |
|
423 xsymlinks(xbuf3); |
|
424 zsfree(*pp); |
|
425 } |
|
426 init_buf++; |
|
427 } |
|
428 free(opp); |
|
429 return ret; |
|
430 } |
|
431 |
|
432 /* |
|
433 * expand symlinks in s, and remove other weird things: |
|
434 * note that this always expands symlinks. |
|
435 */ |
|
436 |
|
437 /**/ |
|
438 char * |
|
439 xsymlink(char *s) |
|
440 { |
|
441 if (*(s+2) != SYM_ROOT) |
|
442 return NULL; |
|
443 *xbuf = '\0'; |
|
444 xsymlinks(s + 3); |
|
445 if (!*xbuf) |
|
446 return ztrdup(s); |
|
447 return ztrdup(xbuf); |
|
448 } |
|
449 |
|
450 /**/ |
|
451 void |
|
452 print_if_link(char *s) |
|
453 { |
|
454 if (*s == '/') { |
|
455 *xbuf = '\0'; |
|
456 if (xsymlinks(s + 1)) |
|
457 printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout); |
|
458 } |
|
459 } |
|
460 |
|
461 /* print a directory */ |
|
462 |
|
463 /**/ |
|
464 void |
|
465 fprintdir(char *s, FILE *f) |
|
466 { |
|
467 Nameddir d = finddir(s); |
|
468 |
|
469 if (!d) |
|
470 fputs(unmeta(s), f); |
|
471 else { |
|
472 #ifndef __SYMBIAN32__ |
|
473 putc('~', f); |
|
474 fputs(unmeta(d->nam), f); |
|
475 #endif |
|
476 fputs(unmeta(s),f); |
|
477 } |
|
478 } |
|
479 |
|
480 /* Returns the current username. It caches the username * |
|
481 * and uid to try to avoid requerying the password files * |
|
482 * or NIS/NIS+ database. */ |
|
483 |
|
484 /**/ |
|
485 uid_t cached_uid; |
|
486 /**/ |
|
487 char *cached_username; |
|
488 |
|
489 /**/ |
|
490 char * |
|
491 get_username(void) |
|
492 { |
|
493 #ifdef HAVE_GETPWUID |
|
494 struct passwd *pswd; |
|
495 uid_t current_uid; |
|
496 |
|
497 current_uid = getuid(); |
|
498 if (current_uid != cached_uid) { |
|
499 cached_uid = current_uid; |
|
500 zsfree(cached_username); |
|
501 if ((pswd = getpwuid(current_uid))) |
|
502 cached_username = ztrdup(pswd->pw_name); |
|
503 else |
|
504 cached_username = ztrdup(""); |
|
505 } |
|
506 #else /* !HAVE_GETPWUID */ |
|
507 cached_uid = getuid(); |
|
508 #endif /* !HAVE_GETPWUID */ |
|
509 return cached_username; |
|
510 } |
|
511 |
|
512 /* static variables needed by finddir(). */ |
|
513 |
|
514 static char *finddir_full; |
|
515 static Nameddir finddir_last; |
|
516 static int finddir_best; |
|
517 |
|
518 /* ScanFunc used by finddir(). */ |
|
519 |
|
520 /**/ |
|
521 static void |
|
522 finddir_scan(HashNode hn, UNUSED(int flags)) |
|
523 { |
|
524 Nameddir nd = (Nameddir) hn; |
|
525 #ifdef __SYMBIAN32__ |
|
526 flags=flags; |
|
527 #endif |
|
528 if(nd->diff > finddir_best && !dircmp(nd->dir, finddir_full) |
|
529 && !(nd->flags & ND_NOABBREV)) { |
|
530 finddir_last=nd; |
|
531 finddir_best=nd->diff; |
|
532 } |
|
533 } |
|
534 |
|
535 /* See if a path has a named directory as its prefix. * |
|
536 * If passed a NULL argument, it will invalidate any * |
|
537 * cached information. */ |
|
538 |
|
539 /**/ |
|
540 Nameddir |
|
541 finddir(char *s) |
|
542 { |
|
543 static struct nameddir homenode = { NULL, "", 0, NULL, 0 }; |
|
544 static int ffsz; |
|
545 |
|
546 /* Invalidate directory cache if argument is NULL. This is called * |
|
547 * whenever a node is added to or removed from the hash table, and * |
|
548 * whenever the value of $HOME changes. (On startup, too.) */ |
|
549 if (!s) { |
|
550 homenode.dir = home; |
|
551 homenode.diff = strlen(home); |
|
552 if(homenode.diff==1) |
|
553 homenode.diff = 0; |
|
554 if(!finddir_full) |
|
555 finddir_full = zalloc(ffsz = PATH_MAX); |
|
556 finddir_full[0] = 0; |
|
557 return finddir_last = NULL; |
|
558 } |
|
559 |
|
560 if(!strcmp(s, finddir_full) && *finddir_full) |
|
561 return finddir_last; |
|
562 |
|
563 if ((int)strlen(s) >= ffsz) { |
|
564 free(finddir_full); |
|
565 finddir_full = zalloc(ffsz = strlen(s) * 2); |
|
566 } |
|
567 strcpy(finddir_full, s); |
|
568 finddir_best=0; |
|
569 finddir_last=NULL; |
|
570 finddir_scan((HashNode)&homenode, 0); |
|
571 scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0); |
|
572 return finddir_last; |
|
573 } |
|
574 |
|
575 /* add a named directory */ |
|
576 |
|
577 /**/ |
|
578 mod_export void |
|
579 adduserdir(char *s, char *t, int flags, int always) |
|
580 { |
|
581 Nameddir nd; |
|
582 |
|
583 /* We don't maintain a hash table in non-interactive shells. */ |
|
584 if (!interact) |
|
585 return; |
|
586 |
|
587 /* The ND_USERNAME flag means that this possible hash table * |
|
588 * entry is derived from a passwd entry. Such entries are * |
|
589 * subordinate to explicitly generated entries. */ |
|
590 if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s)) |
|
591 return; |
|
592 |
|
593 /* Normal parameter assignments generate calls to this function, * |
|
594 * with always==0. Unless the AUTO_NAME_DIRS option is set, we * |
|
595 * don't let such assignments actually create directory names. * |
|
596 * Instead, a reference to the parameter as a directory name can * |
|
597 * cause the actual creation of the hash table entry. */ |
|
598 if (!always && unset(AUTONAMEDIRS) && |
|
599 !nameddirtab->getnode2(nameddirtab, s)) |
|
600 return; |
|
601 |
|
602 if (!t || *t != '/' || strlen(t) >= PATH_MAX) { |
|
603 /* We can't use this value as a directory, so simply remove * |
|
604 * the corresponding entry in the hash table, if any. */ |
|
605 HashNode hn = nameddirtab->removenode(nameddirtab, s); |
|
606 |
|
607 if(hn) |
|
608 nameddirtab->freenode(hn); |
|
609 return; |
|
610 } |
|
611 |
|
612 /* add the name */ |
|
613 nd = (Nameddir) zshcalloc(sizeof *nd); |
|
614 nd->flags = flags; |
|
615 nd->dir = ztrdup(t); |
|
616 /* The variables PWD and OLDPWD are not to be displayed as ~PWD etc. */ |
|
617 if (!strcmp(s, "PWD") || !strcmp(s, "OLDPWD")) |
|
618 nd->flags |= ND_NOABBREV; |
|
619 nameddirtab->addnode(nameddirtab, ztrdup(s), nd); |
|
620 } |
|
621 |
|
622 /* Get a named directory: this function can cause a directory name * |
|
623 * to be added to the hash table, if it isn't there already. */ |
|
624 |
|
625 /**/ |
|
626 char * |
|
627 getnameddir(char *name) |
|
628 { |
|
629 Param pm; |
|
630 char *str; |
|
631 Nameddir nd; |
|
632 |
|
633 /* Check if it is already in the named directory table */ |
|
634 if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name))) |
|
635 return dupstring(nd->dir); |
|
636 |
|
637 /* Check if there is a scalar parameter with this name whose value * |
|
638 * begins with a `/'. If there is, add it to the hash table and * |
|
639 * return the new value. */ |
|
640 if ((pm = (Param) paramtab->getnode(paramtab, name)) && |
|
641 (PM_TYPE(pm->flags) == PM_SCALAR) && |
|
642 (str = getsparam(name)) && *str == '/') { |
|
643 pm->flags |= PM_NAMEDDIR; |
|
644 adduserdir(name, str, 0, 1); |
|
645 return str; |
|
646 } |
|
647 |
|
648 #ifdef HAVE_GETPWNAM |
|
649 { |
|
650 /* Retrieve an entry from the password table/database for this user. */ |
|
651 struct passwd *pw; |
|
652 if ((pw = getpwnam(name))) { |
|
653 char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir) |
|
654 : ztrdup(pw->pw_dir); |
|
655 adduserdir(name, dir, ND_USERNAME, 1); |
|
656 str = dupstring(dir); |
|
657 zsfree(dir); |
|
658 return str; |
|
659 } |
|
660 } |
|
661 #endif /* HAVE_GETPWNAM */ |
|
662 |
|
663 /* There are no more possible sources of directory names, so give up. */ |
|
664 return NULL; |
|
665 } |
|
666 |
|
667 /**/ |
|
668 static int |
|
669 dircmp(char *s, char *t) |
|
670 { |
|
671 if (s) { |
|
672 for (; *s == *t; s++, t++) |
|
673 if (!*s) |
|
674 return 0; |
|
675 if (!*s && (*t == '/' || *t == '\\')) |
|
676 return 0; |
|
677 } |
|
678 return 1; |
|
679 } |
|
680 |
|
681 /* extra functions to call before displaying the prompt */ |
|
682 |
|
683 /**/ |
|
684 mod_export LinkList prepromptfns; |
|
685 |
|
686 /* the last time we checked mail */ |
|
687 |
|
688 /**/ |
|
689 time_t lastmailcheck; |
|
690 |
|
691 /* the last time we checked the people in the WATCH variable */ |
|
692 |
|
693 /**/ |
|
694 time_t lastwatch; |
|
695 |
|
696 /**/ |
|
697 mod_export int |
|
698 callhookfunc(char *name, LinkList lnklst) |
|
699 { |
|
700 Eprog prog; |
|
701 |
|
702 if ((prog = getshfunc(name)) != &dummy_eprog) { |
|
703 /* |
|
704 * Save stopmsg, since user doesn't get a chance to respond |
|
705 * to a list of jobs generated in a hook. |
|
706 */ |
|
707 int osc = sfcontext, osm = stopmsg; |
|
708 |
|
709 sfcontext = SFC_HOOK; |
|
710 doshfunc(name, prog, lnklst, 0, 1); |
|
711 sfcontext = osc; |
|
712 stopmsg = osm; |
|
713 |
|
714 return 0; |
|
715 } |
|
716 |
|
717 return 1; |
|
718 } |
|
719 |
|
720 /* do pre-prompt stuff */ |
|
721 |
|
722 /**/ |
|
723 void |
|
724 preprompt(void) |
|
725 { |
|
726 static time_t lastperiodic; |
|
727 LinkNode ln; |
|
728 int period = getiparam("PERIOD"); |
|
729 int mailcheck = getiparam("MAILCHECK"); |
|
730 |
|
731 /* If NOTIFY is not set, then check for completed * |
|
732 * jobs before we print the prompt. */ |
|
733 if (unset(NOTIFY)) |
|
734 scanjobs(); |
|
735 if (errflag) |
|
736 return; |
|
737 |
|
738 /* If a shell function named "precmd" exists, * |
|
739 * then execute it. */ |
|
740 callhookfunc("precmd", NULL); |
|
741 if (errflag) |
|
742 return; |
|
743 |
|
744 /* If 1) the parameter PERIOD exists, 2) the shell function * |
|
745 * "periodic" exists, 3) it's been greater than PERIOD since we * |
|
746 * executed "periodic", then execute it now. */ |
|
747 if (period && (time(NULL) > lastperiodic + period) && |
|
748 !callhookfunc("periodic", NULL)) |
|
749 lastperiodic = time(NULL); |
|
750 if (errflag) |
|
751 return; |
|
752 |
|
753 /* If WATCH is set, then check for the * |
|
754 * specified login/logout events. */ |
|
755 if (watch) { |
|
756 if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) { |
|
757 #ifndef __SYMBIAN32__ |
|
758 dowatch(); |
|
759 #endif |
|
760 lastwatch = time(NULL); |
|
761 } |
|
762 } |
|
763 if (errflag) |
|
764 return; |
|
765 |
|
766 /* Check mail */ |
|
767 if (mailcheck && (int) difftime(time(NULL), lastmailcheck) > mailcheck) { |
|
768 char *mailfile; |
|
769 |
|
770 if (mailpath && *mailpath && **mailpath) |
|
771 checkmailpath(mailpath); |
|
772 else { |
|
773 queue_signals(); |
|
774 if ((mailfile = getsparam("MAIL")) && *mailfile) { |
|
775 char *x[2]; |
|
776 |
|
777 x[0] = mailfile; |
|
778 x[1] = NULL; |
|
779 checkmailpath(x); |
|
780 } |
|
781 unqueue_signals(); |
|
782 } |
|
783 lastmailcheck = time(NULL); |
|
784 } |
|
785 |
|
786 /* Some people have claimed that C performs type * |
|
787 * checking, but they were later found to be lying. */ |
|
788 for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) |
|
789 (**(void (**) _((void)))getdata(ln))(); |
|
790 } |
|
791 |
|
792 /**/ |
|
793 static void |
|
794 checkmailpath(char **s) |
|
795 { |
|
796 struct stat st; |
|
797 char *v, *u, c; |
|
798 |
|
799 while (*s) { |
|
800 for (v = *s; *v && *v != '?'; v++); |
|
801 c = *v; |
|
802 *v = '\0'; |
|
803 if (c != '?') |
|
804 u = NULL; |
|
805 else |
|
806 u = v + 1; |
|
807 if (**s == 0) { |
|
808 *v = c; |
|
809 zerr("empty MAILPATH component: %s", *s, 0); |
|
810 } else if (mailstat(unmeta(*s), &st) == -1) { |
|
811 if (errno != ENOENT) |
|
812 zerr("%e: %s", *s, errno); |
|
813 } else if (S_ISDIR(st.st_mode)) { |
|
814 LinkList l; |
|
815 DIR *lock = opendir(unmeta(*s)); |
|
816 char buf[PATH_MAX * 2], **arr, **ap; |
|
817 int ct = 1; |
|
818 |
|
819 if (lock) { |
|
820 char *fn; |
|
821 |
|
822 pushheap(); |
|
823 l = newlinklist(); |
|
824 while ((fn = zreaddir(lock, 1)) && !errflag) { |
|
825 if (u) |
|
826 sprintf(buf, "%s/%s?%s", *s, fn, u); |
|
827 else |
|
828 sprintf(buf, "%s/%s", *s, fn); |
|
829 addlinknode(l, dupstring(buf)); |
|
830 ct++; |
|
831 } |
|
832 closedir(lock); |
|
833 ap = arr = (char **) zhalloc(ct * sizeof(char *)); |
|
834 |
|
835 while ((*ap++ = (char *)ugetnode(l))); |
|
836 checkmailpath(arr); |
|
837 popheap(); |
|
838 } |
|
839 } else { |
|
840 if (st.st_size && st.st_atime <= st.st_mtime && |
|
841 st.st_mtime > lastmailcheck) { |
|
842 if (!u) { |
|
843 fprintf(shout, "You have new mail.\n"); |
|
844 fflush(shout); |
|
845 } else { |
|
846 VARARR(char, usav, underscoreused); |
|
847 |
|
848 memcpy(usav, underscore, underscoreused); |
|
849 |
|
850 setunderscore(*s); |
|
851 |
|
852 u = dupstring(u); |
|
853 if (! parsestr(u)) { |
|
854 singsub(&u); |
|
855 zputs(u, shout); |
|
856 fputc('\n', shout); |
|
857 fflush(shout); |
|
858 } |
|
859 setunderscore(usav); |
|
860 } |
|
861 } |
|
862 if (isset(MAILWARNING) && st.st_atime > st.st_mtime && |
|
863 st.st_atime > lastmailcheck && st.st_size) { |
|
864 fprintf(shout, "The mail in %s has been read.\n", unmeta(*s)); |
|
865 fflush(shout); |
|
866 } |
|
867 } |
|
868 *v = c; |
|
869 s++; |
|
870 } |
|
871 } |
|
872 |
|
873 /* This prints the XTRACE prompt. */ |
|
874 |
|
875 /**/ |
|
876 FILE *xtrerr = 0; |
|
877 |
|
878 /**/ |
|
879 void |
|
880 printprompt4(void) |
|
881 { |
|
882 if (!xtrerr) |
|
883 xtrerr = stderr; |
|
884 if (prompt4) { |
|
885 int l, t = opts[XTRACE]; |
|
886 char *s = dupstring(prompt4); |
|
887 |
|
888 opts[XTRACE] = 0; |
|
889 unmetafy(s, &l); |
|
890 s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), 0, NULL, NULL), &l); |
|
891 opts[XTRACE] = t; |
|
892 |
|
893 fprintf(xtrerr, "%s", s); |
|
894 free(s); |
|
895 } |
|
896 } |
|
897 |
|
898 /**/ |
|
899 mod_export void |
|
900 freestr(void *a) |
|
901 { |
|
902 zsfree(a); |
|
903 } |
|
904 |
|
905 /**/ |
|
906 void |
|
907 gettyinfo(struct ttyinfo *ti) |
|
908 { |
|
909 if (SHTTY != -1) { |
|
910 #ifdef HAVE_TERMIOS_H |
|
911 # ifdef HAVE_TCGETATTR |
|
912 if (tcgetattr(SHTTY, &ti->tio) == -1) |
|
913 # else |
|
914 if (ioctl(SHTTY, TCGETS, &ti->tio) == -1) |
|
915 # endif |
|
916 zerr("bad tcgets: %e", NULL, errno); |
|
917 #else |
|
918 # ifdef HAVE_TERMIO_H |
|
919 ioctl(SHTTY, TCGETA, &ti->tio); |
|
920 # else |
|
921 ioctl(SHTTY, TIOCGETP, &ti->sgttyb); |
|
922 ioctl(SHTTY, TIOCLGET, &ti->lmodes); |
|
923 ioctl(SHTTY, TIOCGETC, &ti->tchars); |
|
924 ioctl(SHTTY, TIOCGLTC, &ti->ltchars); |
|
925 # endif |
|
926 #endif |
|
927 } |
|
928 } |
|
929 |
|
930 /**/ |
|
931 mod_export void |
|
932 settyinfo(struct ttyinfo *ti) |
|
933 { |
|
934 if (SHTTY != -1) { |
|
935 #ifdef HAVE_TERMIOS_H |
|
936 # ifdef HAVE_TCGETATTR |
|
937 # ifndef TCSADRAIN |
|
938 # define TCSADRAIN 1 /* XXX Princeton's include files are screwed up */ |
|
939 # endif |
|
940 tcsetattr(SHTTY, TCSADRAIN, &ti->tio); |
|
941 /* if (tcsetattr(SHTTY, TCSADRAIN, &ti->tio) == -1) */ |
|
942 # else |
|
943 ioctl(SHTTY, TCSETS, &ti->tio); |
|
944 /* if (ioctl(SHTTY, TCSETS, &ti->tio) == -1) */ |
|
945 # endif |
|
946 /* zerr("settyinfo: %e",NULL,errno)*/ ; |
|
947 #else |
|
948 # ifdef HAVE_TERMIO_H |
|
949 ioctl(SHTTY, TCSETA, &ti->tio); |
|
950 # else |
|
951 ioctl(SHTTY, TIOCSETN, &ti->sgttyb); |
|
952 ioctl(SHTTY, TIOCLSET, &ti->lmodes); |
|
953 ioctl(SHTTY, TIOCSETC, &ti->tchars); |
|
954 ioctl(SHTTY, TIOCSLTC, &ti->ltchars); |
|
955 # endif |
|
956 #endif |
|
957 } |
|
958 } |
|
959 |
|
960 /* the default tty state */ |
|
961 |
|
962 /**/ |
|
963 mod_export struct ttyinfo shttyinfo; |
|
964 |
|
965 /* != 0 if we need to call resetvideo() */ |
|
966 |
|
967 /**/ |
|
968 mod_export int resetneeded; |
|
969 |
|
970 #ifdef TIOCGWINSZ |
|
971 /* window size changed */ |
|
972 |
|
973 /**/ |
|
974 mod_export int winchanged; |
|
975 #endif |
|
976 |
|
977 static int |
|
978 adjustlines(int signalled) |
|
979 { |
|
980 int oldlines = lines; |
|
981 |
|
982 #ifdef TIOCGWINSZ |
|
983 if (signalled || lines <= 0) |
|
984 lines = shttyinfo.winsize.ws_row; |
|
985 else |
|
986 shttyinfo.winsize.ws_row = lines; |
|
987 #endif /* TIOCGWINSZ */ |
|
988 if (lines <= 0) { |
|
989 DPUTS(signalled, "BUG: Impossible TIOCGWINSZ rows"); |
|
990 lines = tclines > 0 ? tclines : 24; |
|
991 } |
|
992 |
|
993 if (lines > 2) |
|
994 termflags &= ~TERM_SHORT; |
|
995 else |
|
996 termflags |= TERM_SHORT; |
|
997 |
|
998 return (lines != oldlines); |
|
999 } |
|
1000 |
|
1001 static int |
|
1002 adjustcolumns(int signalled) |
|
1003 { |
|
1004 int oldcolumns = columns; |
|
1005 |
|
1006 #ifdef TIOCGWINSZ |
|
1007 if (signalled || columns <= 0) |
|
1008 columns = shttyinfo.winsize.ws_col; |
|
1009 else |
|
1010 shttyinfo.winsize.ws_col = columns; |
|
1011 #endif /* TIOCGWINSZ */ |
|
1012 if (columns <= 0) { |
|
1013 DPUTS(signalled, "BUG: Impossible TIOCGWINSZ cols"); |
|
1014 columns = tccolumns > 0 ? tccolumns : 80; |
|
1015 } |
|
1016 |
|
1017 if (columns > 2) |
|
1018 termflags &= ~TERM_NARROW; |
|
1019 else |
|
1020 termflags |= TERM_NARROW; |
|
1021 |
|
1022 return (columns != oldcolumns); |
|
1023 } |
|
1024 |
|
1025 /* check the size of the window and adjust if necessary. * |
|
1026 * The value of from: * |
|
1027 * 0: called from update_job or setupvals * |
|
1028 * 1: called from the SIGWINCH handler * |
|
1029 * 2: called from the LINES parameter callback * |
|
1030 * 3: called from the COLUMNS parameter callback */ |
|
1031 |
|
1032 /**/ |
|
1033 void |
|
1034 adjustwinsize(int from) |
|
1035 { |
|
1036 static int getwinsz = 1; |
|
1037 int ttyrows = shttyinfo.winsize.ws_row; |
|
1038 int ttycols = shttyinfo.winsize.ws_col; |
|
1039 int resetzle = 0; |
|
1040 |
|
1041 if (getwinsz || from == 1) { |
|
1042 #ifdef TIOCGWINSZ |
|
1043 if (SHTTY == -1) |
|
1044 return; |
|
1045 if (ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize) == 0) { |
|
1046 resetzle = (ttyrows != shttyinfo.winsize.ws_row || |
|
1047 ttycols != shttyinfo.winsize.ws_col); |
|
1048 if (from == 0 && resetzle && ttyrows && ttycols) |
|
1049 from = 1; /* Signal missed while a job owned the tty? */ |
|
1050 ttyrows = shttyinfo.winsize.ws_row; |
|
1051 ttycols = shttyinfo.winsize.ws_col; |
|
1052 } else { |
|
1053 /* Set to value from environment on failure */ |
|
1054 shttyinfo.winsize.ws_row = lines; |
|
1055 shttyinfo.winsize.ws_col = columns; |
|
1056 resetzle = (from == 1); |
|
1057 } |
|
1058 #else |
|
1059 resetzle = from == 1; |
|
1060 #endif /* TIOCGWINSZ */ |
|
1061 } /* else |
|
1062 return; */ |
|
1063 |
|
1064 switch (from) { |
|
1065 case 0: |
|
1066 case 1: |
|
1067 getwinsz = 0; |
|
1068 /* Calling setiparam() here calls this function recursively, but * |
|
1069 * because we've already called adjustlines() and adjustcolumns() * |
|
1070 * here, recursive calls are no-ops unless a signal intervenes. * |
|
1071 * The commented "else return;" above might be a safe shortcut, * |
|
1072 * but I'm concerned about what happens on race conditions; e.g., * |
|
1073 * suppose the user resizes his xterm during `eval $(resize)'? */ |
|
1074 if (adjustlines(from) && zgetenv("LINES")) |
|
1075 setiparam("LINES", lines); |
|
1076 if (adjustcolumns(from) && zgetenv("COLUMNS")) |
|
1077 setiparam("COLUMNS", columns); |
|
1078 getwinsz = 1; |
|
1079 break; |
|
1080 case 2: |
|
1081 resetzle = adjustlines(0); |
|
1082 break; |
|
1083 case 3: |
|
1084 resetzle = adjustcolumns(0); |
|
1085 break; |
|
1086 } |
|
1087 |
|
1088 #ifdef TIOCGWINSZ |
|
1089 if (interact && from >= 2 && |
|
1090 (shttyinfo.winsize.ws_row != ttyrows || |
|
1091 shttyinfo.winsize.ws_col != ttycols)) { |
|
1092 /* shttyinfo.winsize is already set up correctly */ |
|
1093 ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize); |
|
1094 } |
|
1095 #endif /* TIOCGWINSZ */ |
|
1096 |
|
1097 if (zleactive && resetzle) { |
|
1098 #ifdef TIOCGWINSZ |
|
1099 winchanged = |
|
1100 #endif /* TIOCGWINSZ */ |
|
1101 resetneeded = 1; |
|
1102 zrefresh(); |
|
1103 zle_resetprompt(); |
|
1104 } |
|
1105 } |
|
1106 |
|
1107 /* Move a fd to a place >= 10 and mark the new fd in fdtable. If the fd * |
|
1108 * is already >= 10, it is not moved. If it is invalid, -1 is returned. */ |
|
1109 |
|
1110 /**/ |
|
1111 mod_export int |
|
1112 movefd(int fd) |
|
1113 { |
|
1114 if(fd != -1 && fd < 10) { |
|
1115 #ifdef F_DUPFD |
|
1116 int fe = fcntl(fd, F_DUPFD, 10); |
|
1117 #else |
|
1118 int fe = movefd(dup(fd)); |
|
1119 #endif |
|
1120 zclose(fd); |
|
1121 fd = fe; |
|
1122 } |
|
1123 if(fd != -1) { |
|
1124 if (fd > max_zsh_fd) { |
|
1125 while (fd >= fdtable_size) |
|
1126 fdtable = zrealloc(fdtable, (fdtable_size *= 2)); |
|
1127 max_zsh_fd = fd; |
|
1128 } |
|
1129 fdtable[fd] = 1; |
|
1130 } |
|
1131 return fd; |
|
1132 } |
|
1133 |
|
1134 /* Move fd x to y. If x == -1, fd y is closed. */ |
|
1135 |
|
1136 /**/ |
|
1137 mod_export void |
|
1138 redup(int x, int y) |
|
1139 { |
|
1140 if(x < 0) |
|
1141 zclose(y); |
|
1142 else if (x != y) { |
|
1143 while (y >= fdtable_size) |
|
1144 fdtable = zrealloc(fdtable, (fdtable_size *= 2)); |
|
1145 dup2(x, y); |
|
1146 if ((fdtable[y] = fdtable[x]) && y > max_zsh_fd) |
|
1147 max_zsh_fd = y; |
|
1148 zclose(x); |
|
1149 } |
|
1150 } |
|
1151 |
|
1152 /* Close the given fd, and clear it from fdtable. */ |
|
1153 |
|
1154 /**/ |
|
1155 mod_export int |
|
1156 zclose(int fd) |
|
1157 { |
|
1158 if (fd >= 0) { |
|
1159 fdtable[fd] = 0; |
|
1160 while (max_zsh_fd > 0 && !fdtable[max_zsh_fd]) |
|
1161 max_zsh_fd--; |
|
1162 if (fd == coprocin) |
|
1163 coprocin = -1; |
|
1164 if (fd == coprocout) |
|
1165 coprocout = -1; |
|
1166 return close(fd); |
|
1167 } |
|
1168 return -1; |
|
1169 } |
|
1170 |
|
1171 #ifdef HAVE__MKTEMP |
|
1172 extern char *_mktemp(char *); |
|
1173 #endif |
|
1174 |
|
1175 /* Get a unique filename for use as a temporary file. If "prefix" is |
|
1176 * NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the |
|
1177 * unique suffix includes a prefixed '.' for improved readability. If |
|
1178 * "use_heap" is true, we allocate the returned name on the heap. */ |
|
1179 |
|
1180 /**/ |
|
1181 mod_export char * |
|
1182 gettempname(const char *prefix, int use_heap) |
|
1183 { |
|
1184 char *ret, *suffix = prefix ? ".XXXXXX" : "XXXXXX"; |
|
1185 |
|
1186 queue_signals(); |
|
1187 #ifndef __SYMBIAN32__ |
|
1188 if (!prefix && !(prefix = getsparam("TMPPREFIX"))) |
|
1189 #endif |
|
1190 prefix = DEFAULT_TMPPREFIX; |
|
1191 if (use_heap) |
|
1192 ret = dyncat(unmeta(prefix), suffix); |
|
1193 else |
|
1194 ret = bicat(unmeta(prefix), suffix); |
|
1195 |
|
1196 #ifdef HAVE__MKTEMP |
|
1197 /* Zsh uses mktemp() safely, so silence the warnings */ |
|
1198 ret = (char *) _mktemp(ret); |
|
1199 #else |
|
1200 ret = (char *) mktemp(ret); |
|
1201 #endif |
|
1202 unqueue_signals(); |
|
1203 |
|
1204 return ret; |
|
1205 } |
|
1206 |
|
1207 /**/ |
|
1208 mod_export int |
|
1209 gettempfile(const char *prefix, int use_heap, char **tempname) |
|
1210 { |
|
1211 char *fn; |
|
1212 int fd; |
|
1213 #if HAVE_MKSTEMP |
|
1214 char *suffix = prefix ? ".XXXXXX" : "XXXXXX"; |
|
1215 #ifndef __SYMBIAN32__ |
|
1216 if (!prefix && !(prefix = getsparam("TMPPREFIX"))) |
|
1217 #endif |
|
1218 prefix = DEFAULT_TMPPREFIX; |
|
1219 if (use_heap) |
|
1220 fn = dyncat(unmeta(prefix), suffix); |
|
1221 else |
|
1222 fn = bicat(unmeta(prefix), suffix); |
|
1223 |
|
1224 fd = mkstemp(fn); |
|
1225 if (fd < 0) { |
|
1226 if (!use_heap) |
|
1227 free(fn); |
|
1228 fn = NULL; |
|
1229 } |
|
1230 #else |
|
1231 int failures = 0; |
|
1232 |
|
1233 do { |
|
1234 if (!(fn = gettempname(prefix, use_heap))) { |
|
1235 fd = -1; |
|
1236 break; |
|
1237 } |
|
1238 if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0) |
|
1239 break; |
|
1240 if (!use_heap) |
|
1241 free(fn); |
|
1242 fn = NULL; |
|
1243 } while (errno == EEXIST && ++failures < 16); |
|
1244 #endif |
|
1245 *tempname = fn; |
|
1246 return fd; |
|
1247 } |
|
1248 |
|
1249 /* Check if a string contains a token */ |
|
1250 |
|
1251 /**/ |
|
1252 mod_export int |
|
1253 has_token(const char *s) |
|
1254 { |
|
1255 while(*s) |
|
1256 if(itok(*s++)) |
|
1257 return 1; |
|
1258 return 0; |
|
1259 } |
|
1260 |
|
1261 /* Delete a character in a string */ |
|
1262 |
|
1263 /**/ |
|
1264 mod_export void |
|
1265 chuck(char *str) |
|
1266 { |
|
1267 while ((str[0] = str[1])) |
|
1268 str++; |
|
1269 } |
|
1270 |
|
1271 /**/ |
|
1272 mod_export int |
|
1273 tulower(int c) |
|
1274 { |
|
1275 c &= 0xff; |
|
1276 return (isupper(c) ? tolower(c) : c); |
|
1277 } |
|
1278 |
|
1279 /**/ |
|
1280 mod_export int |
|
1281 tuupper(int c) |
|
1282 { |
|
1283 c &= 0xff; |
|
1284 return (islower(c) ? toupper(c) : c); |
|
1285 } |
|
1286 |
|
1287 /* copy len chars from t into s, and null terminate */ |
|
1288 |
|
1289 /**/ |
|
1290 void |
|
1291 ztrncpy(char *s, char *t, int len) |
|
1292 { |
|
1293 while (len--) |
|
1294 *s++ = *t++; |
|
1295 *s = '\0'; |
|
1296 } |
|
1297 |
|
1298 /* copy t into *s and update s */ |
|
1299 |
|
1300 /**/ |
|
1301 mod_export void |
|
1302 strucpy(char **s, char *t) |
|
1303 { |
|
1304 char *u = *s; |
|
1305 |
|
1306 while ((*u++ = *t++)); |
|
1307 *s = u - 1; |
|
1308 } |
|
1309 |
|
1310 /**/ |
|
1311 mod_export void |
|
1312 struncpy(char **s, char *t, int n) |
|
1313 { |
|
1314 char *u = *s; |
|
1315 |
|
1316 while (n--) |
|
1317 *u++ = *t++; |
|
1318 *s = u; |
|
1319 *u = '\0'; |
|
1320 } |
|
1321 |
|
1322 /* Return the number of elements in an array of pointers. * |
|
1323 * It doesn't count the NULL pointer at the end. */ |
|
1324 |
|
1325 /**/ |
|
1326 mod_export int |
|
1327 arrlen(char **s) |
|
1328 { |
|
1329 int count = 0; |
|
1330 |
|
1331 if(NULL != s) |
|
1332 { |
|
1333 for (; *s; s++, count++); |
|
1334 } |
|
1335 |
|
1336 return count; |
|
1337 } |
|
1338 |
|
1339 /* Skip over a balanced pair of parenthesis. */ |
|
1340 |
|
1341 /**/ |
|
1342 mod_export int |
|
1343 skipparens(char inpar, char outpar, char **s) |
|
1344 { |
|
1345 int level; |
|
1346 |
|
1347 if (**s != inpar) |
|
1348 return -1; |
|
1349 |
|
1350 for (level = 1; *++*s && level;) |
|
1351 if (**s == inpar) |
|
1352 ++level; |
|
1353 else if (**s == outpar) |
|
1354 --level; |
|
1355 |
|
1356 return level; |
|
1357 } |
|
1358 |
|
1359 /* Convert string to zlong (see zsh.h). This function (without the z) * |
|
1360 * is contained in the ANSI standard C library, but a lot of them seem * |
|
1361 * to be broken. */ |
|
1362 |
|
1363 /**/ |
|
1364 mod_export zlong |
|
1365 zstrtol(const char *s, char **t, int base) |
|
1366 { |
|
1367 const char *inp, *trunc = NULL; |
|
1368 zulong calc = 0, newcalc = 0; |
|
1369 int neg; |
|
1370 |
|
1371 while (inblank(*s)) |
|
1372 s++; |
|
1373 |
|
1374 if ((neg = (*s == '-'))) |
|
1375 s++; |
|
1376 else if (*s == '+') |
|
1377 s++; |
|
1378 |
|
1379 if (!base) { |
|
1380 if (*s != '0') |
|
1381 base = 10; |
|
1382 else if (*++s == 'x' || *s == 'X') |
|
1383 base = 16, s++; |
|
1384 else |
|
1385 base = 8; |
|
1386 } |
|
1387 inp = s; |
|
1388 if (base <= 10) |
|
1389 for (; *s >= '0' && *s < ('0' + base); s++) { |
|
1390 if (trunc) |
|
1391 continue; |
|
1392 newcalc = calc * base + *s - '0'; |
|
1393 if (newcalc < calc) |
|
1394 { |
|
1395 trunc = s; |
|
1396 continue; |
|
1397 } |
|
1398 calc = newcalc; |
|
1399 } |
|
1400 else |
|
1401 for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10)) |
|
1402 || (*s >= 'A' && *s < ('A' + base - 10)); s++) { |
|
1403 if (trunc) |
|
1404 continue; |
|
1405 newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); |
|
1406 if (newcalc < calc) |
|
1407 { |
|
1408 trunc = s; |
|
1409 continue; |
|
1410 } |
|
1411 calc = newcalc; |
|
1412 } |
|
1413 |
|
1414 /* |
|
1415 * Special case: check for a number that was just too long for |
|
1416 * signed notation. |
|
1417 * Extra special case: the lowest negative number would trigger |
|
1418 * the first test, but is actually representable correctly. |
|
1419 * This is a 1 in the top bit, all others zero, so test for |
|
1420 * that explicitly. |
|
1421 */ |
|
1422 if (!trunc && (zlong)calc < 0 && |
|
1423 (!neg || calc & ~((zulong)1 << (8*sizeof(zulong)-1)))) |
|
1424 { |
|
1425 trunc = s - 1; |
|
1426 calc /= base; |
|
1427 } |
|
1428 |
|
1429 if (trunc) |
|
1430 zwarn("number truncated after %d digits: %s", inp, trunc - inp); |
|
1431 |
|
1432 if (t) |
|
1433 *t = (char *)s; |
|
1434 return neg ? -(zlong)calc : (zlong)calc; |
|
1435 } |
|
1436 |
|
1437 /**/ |
|
1438 int |
|
1439 setblock_fd(int turnonblocking, int fd, long *modep) |
|
1440 { |
|
1441 #ifdef O_NDELAY |
|
1442 # ifdef O_NONBLOCK |
|
1443 # define NONBLOCK (O_NDELAY|O_NONBLOCK) |
|
1444 # else /* !O_NONBLOCK */ |
|
1445 # define NONBLOCK O_NDELAY |
|
1446 # endif /* !O_NONBLOCK */ |
|
1447 #else /* !O_NDELAY */ |
|
1448 # ifdef O_NONBLOCK |
|
1449 # define NONBLOCK O_NONBLOCK |
|
1450 # else /* !O_NONBLOCK */ |
|
1451 # define NONBLOCK 0 |
|
1452 # endif /* !O_NONBLOCK */ |
|
1453 #endif /* !O_NDELAY */ |
|
1454 |
|
1455 #if NONBLOCK |
|
1456 struct stat st; |
|
1457 |
|
1458 if (!fstat(fd, &st) && !S_ISREG(st.st_mode)) { |
|
1459 *modep = fcntl(fd, F_GETFL, 0); |
|
1460 if (*modep != -1) { |
|
1461 if (!turnonblocking) { |
|
1462 /* We want to know if blocking was off */ |
|
1463 if ((*modep & NONBLOCK) || |
|
1464 !fcntl(fd, F_SETFL, *modep | NONBLOCK)) |
|
1465 return 1; |
|
1466 } else if ((*modep & NONBLOCK) && |
|
1467 !fcntl(fd, F_SETFL, *modep & ~NONBLOCK)) { |
|
1468 /* Here we want to know if the state changed */ |
|
1469 return 1; |
|
1470 } |
|
1471 } |
|
1472 } else |
|
1473 #endif /* NONBLOCK */ |
|
1474 *modep = -1; |
|
1475 return 0; |
|
1476 |
|
1477 #undef NONBLOCK |
|
1478 } |
|
1479 |
|
1480 /**/ |
|
1481 int |
|
1482 setblock_stdin(void) |
|
1483 { |
|
1484 long mode; |
|
1485 return setblock_fd(1, 0, &mode); |
|
1486 } |
|
1487 |
|
1488 /* |
|
1489 * Check for pending input on fd. If polltty is set, we may need to |
|
1490 * use termio to look for input. As a final resort, go to non-blocking |
|
1491 * input and try to read a character, which in this case will be |
|
1492 * returned in *readchar. |
|
1493 * |
|
1494 * Note that apart from setting (and restoring) non-blocking input, |
|
1495 * this function does not change the input mode. The calling function |
|
1496 * should have set cbreak mode if necessary. |
|
1497 */ |
|
1498 |
|
1499 /**/ |
|
1500 mod_export int |
|
1501 read_poll(int fd, int *readchar, int polltty, zlong microseconds) |
|
1502 { |
|
1503 int ret = -1; |
|
1504 long mode = -1; |
|
1505 char c; |
|
1506 #ifdef HAVE_SELECT |
|
1507 fd_set foofd; |
|
1508 struct timeval expire_tv; |
|
1509 #else |
|
1510 #ifdef FIONREAD |
|
1511 int val; |
|
1512 #endif |
|
1513 #endif |
|
1514 #ifdef HAS_TIO |
|
1515 struct ttyinfo ti; |
|
1516 #endif |
|
1517 |
|
1518 |
|
1519 #if defined(HAS_TIO) && !defined(__CYGWIN__) |
|
1520 /* |
|
1521 * Under Solaris, at least, reading from the terminal in non-canonical |
|
1522 * mode requires that we use the VMIN mechanism to poll. Any attempt |
|
1523 * to check any other way, or to set the terminal to non-blocking mode |
|
1524 * and poll that way, fails; it will just for canonical mode input. |
|
1525 * We should probably use this mechanism if the user has set non-canonical |
|
1526 * mode, in which case testing here for isatty() and ~ICANON would be |
|
1527 * better than testing whether bin_read() set it, but for now we've got |
|
1528 * enough problems. |
|
1529 * |
|
1530 * Under Cygwin, you won't be surprised to here, this mechanism, |
|
1531 * although present, doesn't work, and we *have* to use ordinary |
|
1532 * non-blocking reads to find out if there is a character present |
|
1533 * in non-canonical mode. |
|
1534 * |
|
1535 * I am assuming Solaris is nearer the UNIX norm. This is not necessarily |
|
1536 * as plausible as it sounds, but it seems the right way to guess. |
|
1537 * pws 2000/06/26 |
|
1538 */ |
|
1539 if (polltty) { |
|
1540 gettyinfo(&ti); |
|
1541 if ((polltty = ti.tio.c_cc[VMIN])) { |
|
1542 ti.tio.c_cc[VMIN] = 0; |
|
1543 /* termios timeout is 10ths of a second */ |
|
1544 ti.tio.c_cc[VTIME] = (int) (microseconds / (zlong)100000); |
|
1545 settyinfo(&ti); |
|
1546 } |
|
1547 } |
|
1548 #else |
|
1549 polltty = 0; |
|
1550 #endif |
|
1551 #ifdef HAVE_SELECT |
|
1552 expire_tv.tv_sec = (int) (microseconds / (zlong)1000000); |
|
1553 expire_tv.tv_usec = microseconds % (zlong)1000000; |
|
1554 FD_ZERO(&foofd); |
|
1555 FD_SET(fd, &foofd); |
|
1556 ret = select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv); |
|
1557 #else |
|
1558 #ifdef FIONREAD |
|
1559 if (ioctl(fd, FIONREAD, (char *) &val) == 0) |
|
1560 ret = (val > 0); |
|
1561 #endif |
|
1562 #endif |
|
1563 |
|
1564 if (ret < 0) { |
|
1565 /* |
|
1566 * Final attempt: set non-blocking read and try to read a character. |
|
1567 * Praise Bill, this works under Cygwin (nothing else seems to). |
|
1568 */ |
|
1569 if ((polltty || setblock_fd(0, fd, &mode)) && read(fd, &c, 1) > 0) { |
|
1570 *readchar = STOUC(c); |
|
1571 ret = 1; |
|
1572 } |
|
1573 if (mode != -1) |
|
1574 fcntl(fd, F_SETFL, mode); |
|
1575 } |
|
1576 #ifdef HAS_TIO |
|
1577 if (polltty) { |
|
1578 ti.tio.c_cc[VMIN] = 1; |
|
1579 ti.tio.c_cc[VTIME] = 0; |
|
1580 settyinfo(&ti); |
|
1581 } |
|
1582 #endif |
|
1583 return (ret > 0); |
|
1584 } |
|
1585 |
|
1586 /**/ |
|
1587 int |
|
1588 checkrmall(char *s) |
|
1589 { |
|
1590 if (!shout) |
|
1591 return 1; |
|
1592 fprintf(shout, "zsh: sure you want to delete all the files in "); |
|
1593 if (*s != '/') { |
|
1594 nicezputs(pwd[1] ? unmeta(pwd) : "", shout); |
|
1595 fputc('/', shout); |
|
1596 } |
|
1597 nicezputs(s, shout); |
|
1598 if(isset(RMSTARWAIT)) { |
|
1599 fputs("? (waiting ten seconds)", shout); |
|
1600 fflush(shout); |
|
1601 zbeep(); |
|
1602 sleep(10); |
|
1603 fputc('\n', shout); |
|
1604 } |
|
1605 fputs(" [yn]? ", shout); |
|
1606 fflush(shout); |
|
1607 zbeep(); |
|
1608 return (getquery("ny", 1) == 'y'); |
|
1609 } |
|
1610 |
|
1611 /**/ |
|
1612 int |
|
1613 read1char(void) |
|
1614 { |
|
1615 char c; |
|
1616 |
|
1617 while (read(SHTTY, &c, 1) != 1) { |
|
1618 if (errno != EINTR || errflag || retflag || breaks || contflag) |
|
1619 return -1; |
|
1620 } |
|
1621 return STOUC(c); |
|
1622 } |
|
1623 |
|
1624 /**/ |
|
1625 mod_export int |
|
1626 noquery(int purge) |
|
1627 { |
|
1628 int val = 0; |
|
1629 char c; |
|
1630 |
|
1631 #ifdef FIONREAD |
|
1632 ioctl(SHTTY, FIONREAD, (char *)&val); |
|
1633 if (purge) { |
|
1634 for (; val; val--) |
|
1635 read(SHTTY, &c, 1); |
|
1636 } |
|
1637 #endif |
|
1638 |
|
1639 return val; |
|
1640 } |
|
1641 |
|
1642 /**/ |
|
1643 int |
|
1644 getquery(char *valid_chars, int purge) |
|
1645 { |
|
1646 int c, d; |
|
1647 int isem = !strcmp(term, "emacs"); |
|
1648 |
|
1649 attachtty(mypgrp); |
|
1650 if (!isem) |
|
1651 setcbreak(); |
|
1652 |
|
1653 if (noquery(purge)) { |
|
1654 if (!isem) |
|
1655 settyinfo(&shttyinfo); |
|
1656 write(SHTTY, "n\n", 2); |
|
1657 return 'n'; |
|
1658 } |
|
1659 |
|
1660 while ((c = read1char()) >= 0) { |
|
1661 if (c == 'Y') |
|
1662 c = 'y'; |
|
1663 else if (c == 'N') |
|
1664 c = 'n'; |
|
1665 if (!valid_chars) |
|
1666 break; |
|
1667 if (c == '\n') { |
|
1668 c = *valid_chars; |
|
1669 break; |
|
1670 } |
|
1671 if (strchr(valid_chars, c)) { |
|
1672 write(SHTTY, "\n", 1); |
|
1673 break; |
|
1674 } |
|
1675 zbeep(); |
|
1676 if (icntrl(c)) |
|
1677 write(SHTTY, "\b \b", 3); |
|
1678 write(SHTTY, "\b \b", 3); |
|
1679 } |
|
1680 if (isem) { |
|
1681 if (c != '\n') |
|
1682 while ((d = read1char()) >= 0 && d != '\n'); |
|
1683 } else { |
|
1684 settyinfo(&shttyinfo); |
|
1685 if (c != '\n' && !valid_chars) |
|
1686 write(SHTTY, "\n", 1); |
|
1687 } |
|
1688 return c; |
|
1689 } |
|
1690 |
|
1691 static int d; |
|
1692 static char *guess, *best; |
|
1693 |
|
1694 /**/ |
|
1695 static void |
|
1696 spscan(HashNode hn, UNUSED(int scanflags)) |
|
1697 { |
|
1698 int nd; |
|
1699 #ifdef __SYMBIAN32__ |
|
1700 scanflags=scanflags; |
|
1701 #endif |
|
1702 nd = spdist(hn->nam, guess, (int) strlen(guess) / 4 + 1); |
|
1703 if (nd <= d) { |
|
1704 best = hn->nam; |
|
1705 d = nd; |
|
1706 } |
|
1707 } |
|
1708 |
|
1709 /* spellcheck a word */ |
|
1710 /* fix s ; if hist is nonzero, fix the history list too */ |
|
1711 |
|
1712 /**/ |
|
1713 mod_export void |
|
1714 spckword(char **s, int hist, int cmd, int ask) |
|
1715 { |
|
1716 char *t, *u; |
|
1717 int x; |
|
1718 char ic = '\0'; |
|
1719 int ne; |
|
1720 int preflen = 0; |
|
1721 |
|
1722 if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%') |
|
1723 return; |
|
1724 if (!strcmp(*s, "in")) |
|
1725 return; |
|
1726 if (!(*s)[0] || !(*s)[1]) |
|
1727 return; |
|
1728 if (shfunctab->getnode(shfunctab, *s) || |
|
1729 builtintab->getnode(builtintab, *s) || |
|
1730 cmdnamtab->getnode(cmdnamtab, *s) || |
|
1731 aliastab->getnode(aliastab, *s) || |
|
1732 reswdtab->getnode(reswdtab, *s)) |
|
1733 return; |
|
1734 else if (isset(HASHLISTALL)) { |
|
1735 cmdnamtab->filltable(cmdnamtab); |
|
1736 if (cmdnamtab->getnode(cmdnamtab, *s)) |
|
1737 return; |
|
1738 } |
|
1739 t = *s; |
|
1740 if (*t == Tilde || *t == Equals || *t == String) |
|
1741 t++; |
|
1742 for (; *t; t++) |
|
1743 if (itok(*t)) |
|
1744 return; |
|
1745 best = NULL; |
|
1746 for (t = *s; *t; t++) |
|
1747 if (*t == '/') |
|
1748 break; |
|
1749 if (**s == Tilde && !*t) |
|
1750 return; |
|
1751 if (**s == String && !*t) { |
|
1752 guess = *s + 1; |
|
1753 if (*t || !ialpha(*guess)) |
|
1754 return; |
|
1755 ic = String; |
|
1756 d = 100; |
|
1757 scanhashtable(paramtab, 1, 0, 0, spscan, 0); |
|
1758 } else if (**s == Equals) { |
|
1759 if (*t) |
|
1760 return; |
|
1761 if (hashcmd(guess = *s + 1, pathchecked)) |
|
1762 return; |
|
1763 d = 100; |
|
1764 ic = Equals; |
|
1765 scanhashtable(aliastab, 1, 0, 0, spscan, 0); |
|
1766 scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0); |
|
1767 } else { |
|
1768 guess = *s; |
|
1769 if (*guess == Tilde || *guess == String) { |
|
1770 ic = *guess; |
|
1771 if (!*++t) |
|
1772 return; |
|
1773 guess = dupstring(guess); |
|
1774 ne = noerrs; |
|
1775 noerrs = 2; |
|
1776 singsub(&guess); |
|
1777 noerrs = ne; |
|
1778 if (!guess) |
|
1779 return; |
|
1780 preflen = strlen(guess) - strlen(t); |
|
1781 } |
|
1782 if (access(unmeta(guess), F_OK) == 0) |
|
1783 return; |
|
1784 if ((u = spname(guess)) != guess) |
|
1785 best = u; |
|
1786 if (!*t && cmd) { |
|
1787 if (hashcmd(guess, pathchecked)) |
|
1788 return; |
|
1789 d = 100; |
|
1790 scanhashtable(reswdtab, 1, 0, 0, spscan, 0); |
|
1791 scanhashtable(aliastab, 1, 0, 0, spscan, 0); |
|
1792 scanhashtable(shfunctab, 1, 0, 0, spscan, 0); |
|
1793 scanhashtable(builtintab, 1, 0, 0, spscan, 0); |
|
1794 scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0); |
|
1795 } |
|
1796 } |
|
1797 if (errflag) |
|
1798 return; |
|
1799 if (best && (int)strlen(best) > 1 && strcmp(best, guess)) { |
|
1800 if (ic) { |
|
1801 if (preflen) { |
|
1802 /* do not correct the result of an expansion */ |
|
1803 if (strncmp(guess, best, preflen)) |
|
1804 return; |
|
1805 /* replace the temporarily expanded prefix with the original */ |
|
1806 u = (char *) hcalloc(t - *s + strlen(best + preflen) + 1); |
|
1807 strncpy(u, *s, t - *s); |
|
1808 strcpy(u + (t - *s), best + preflen); |
|
1809 } else { |
|
1810 u = (char *) hcalloc(strlen(best) + 2); |
|
1811 strcpy(u + 1, best); |
|
1812 } |
|
1813 best = u; |
|
1814 guess = *s; |
|
1815 *guess = *best = ztokens[ic - Pound]; |
|
1816 } |
|
1817 if (ask) { |
|
1818 if (noquery(0)) { |
|
1819 x = 'n'; |
|
1820 } else { |
|
1821 char *pptbuf; |
|
1822 pptbuf = promptexpand(sprompt, 0, best, guess); |
|
1823 zputs(pptbuf, shout); |
|
1824 free(pptbuf); |
|
1825 fflush(shout); |
|
1826 zbeep(); |
|
1827 x = getquery("nyae \t", 0); |
|
1828 } |
|
1829 } else |
|
1830 x = 'y'; |
|
1831 if (x == 'y' || x == ' ' || x == '\t') { |
|
1832 *s = dupstring(best); |
|
1833 if (hist) |
|
1834 hwrep(best); |
|
1835 } else if (x == 'a') { |
|
1836 histdone |= HISTFLAG_NOEXEC; |
|
1837 } else if (x == 'e') { |
|
1838 histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL; |
|
1839 } |
|
1840 if (ic) |
|
1841 **s = ic; |
|
1842 } |
|
1843 } |
|
1844 |
|
1845 /* |
|
1846 * Helper for ztrftime. Called with a pointer to the length left |
|
1847 * in the buffer, and a new string length to decrement from that. |
|
1848 * Returns 0 if the new length fits, 1 otherwise. We assume a terminating |
|
1849 * NUL and return 1 if that doesn't fit. |
|
1850 */ |
|
1851 |
|
1852 /**/ |
|
1853 static int |
|
1854 ztrftimebuf(int *bufsizeptr, int decr) |
|
1855 { |
|
1856 if (*bufsizeptr <= decr) |
|
1857 return 1; |
|
1858 *bufsizeptr -= decr; |
|
1859 return 0; |
|
1860 } |
|
1861 |
|
1862 /* |
|
1863 * Like the system function, this returns the number of characters |
|
1864 * copied, not including the terminating NUL. This may be zero |
|
1865 * if the string didn't fit. |
|
1866 * |
|
1867 * As an extension, try to detect an error in strftime --- typically |
|
1868 * not enough memory --- and return -1. Not guaranteed to be portable, |
|
1869 * since the strftime() interface doesn't make any guarantees about |
|
1870 * the state of the buffer if it returns zero. |
|
1871 */ |
|
1872 |
|
1873 /**/ |
|
1874 mod_export int |
|
1875 ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm) |
|
1876 { |
|
1877 int hr12, decr; |
|
1878 #ifndef HAVE_STRFTIME |
|
1879 static char *astr[] = |
|
1880 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
|
1881 static char *estr[] = |
|
1882 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", |
|
1883 "Aug", "Sep", "Oct", "Nov", "Dec"}; |
|
1884 #endif |
|
1885 char *origbuf = buf; |
|
1886 char tmp[3]; |
|
1887 |
|
1888 |
|
1889 tmp[0] = '%'; |
|
1890 tmp[2] = '\0'; |
|
1891 while (*fmt) |
|
1892 if (*fmt == '%') { |
|
1893 fmt++; |
|
1894 /* |
|
1895 * Assume this format will take up at least two |
|
1896 * characters. Not always true, but if that matters |
|
1897 * we are so close to the edge it's not a big deal. |
|
1898 * Fix up some longer cases specially when we get to them. |
|
1899 */ |
|
1900 if (ztrftimebuf(&bufsize, 2)) |
|
1901 return 0; |
|
1902 switch (*fmt++) { |
|
1903 case 'd': |
|
1904 *buf++ = '0' + tm->tm_mday / 10; |
|
1905 *buf++ = '0' + tm->tm_mday % 10; |
|
1906 break; |
|
1907 case 'e': |
|
1908 case 'f': |
|
1909 if (tm->tm_mday > 9) |
|
1910 *buf++ = '0' + tm->tm_mday / 10; |
|
1911 else if (fmt[-1] == 'e') |
|
1912 *buf++ = ' '; |
|
1913 *buf++ = '0' + tm->tm_mday % 10; |
|
1914 break; |
|
1915 case 'k': |
|
1916 case 'K': |
|
1917 if (tm->tm_hour > 9) |
|
1918 *buf++ = '0' + tm->tm_hour / 10; |
|
1919 else if (fmt[-1] == 'k') |
|
1920 *buf++ = ' '; |
|
1921 *buf++ = '0' + tm->tm_hour % 10; |
|
1922 break; |
|
1923 case 'l': |
|
1924 case 'L': |
|
1925 hr12 = tm->tm_hour % 12; |
|
1926 if (hr12 == 0) |
|
1927 hr12 = 12; |
|
1928 if (hr12 > 9) |
|
1929 *buf++ = '1'; |
|
1930 else if (fmt[-1] == 'l') |
|
1931 *buf++ = ' '; |
|
1932 |
|
1933 *buf++ = '0' + (hr12 % 10); |
|
1934 break; |
|
1935 case 'm': |
|
1936 *buf++ = '0' + (tm->tm_mon + 1) / 10; |
|
1937 *buf++ = '0' + (tm->tm_mon + 1) % 10; |
|
1938 break; |
|
1939 case 'M': |
|
1940 *buf++ = '0' + tm->tm_min / 10; |
|
1941 *buf++ = '0' + tm->tm_min % 10; |
|
1942 break; |
|
1943 case 'S': |
|
1944 *buf++ = '0' + tm->tm_sec / 10; |
|
1945 *buf++ = '0' + tm->tm_sec % 10; |
|
1946 break; |
|
1947 case 'y': |
|
1948 *buf++ = '0' + (tm->tm_year / 10) % 10; |
|
1949 *buf++ = '0' + tm->tm_year % 10; |
|
1950 break; |
|
1951 case '\0': |
|
1952 /* Guard against premature end of string */ |
|
1953 *buf++ = '%'; |
|
1954 fmt--; |
|
1955 break; |
|
1956 #ifndef HAVE_STRFTIME |
|
1957 case 'a': |
|
1958 if (ztrftimebuf(&bufsize, strlen(astr[tm->tm_wday]) - 2)) |
|
1959 return 0; |
|
1960 strucpy(&buf, astr[tm->tm_wday]); |
|
1961 break; |
|
1962 case 'b': |
|
1963 if (ztrftimebuf(&bufsize, strlen(estr[tm->tm_mon]) - 2)) |
|
1964 return 0; |
|
1965 strucpy(&buf, estr[tm->tm_mon]); |
|
1966 break; |
|
1967 case 'p': |
|
1968 *buf++ = (tm->tm_hour > 11) ? 'p' : 'a'; |
|
1969 *buf++ = 'm'; |
|
1970 break; |
|
1971 default: |
|
1972 *buf++ = '%'; |
|
1973 if (fmt[-1] != '%') |
|
1974 *buf++ = fmt[-1]; |
|
1975 #else |
|
1976 default: |
|
1977 /* |
|
1978 * Remember we've already allowed for two characters |
|
1979 * in the accounting in bufsize (but nowhere else). |
|
1980 */ |
|
1981 *buf = '\1'; |
|
1982 tmp[1] = fmt[-1]; |
|
1983 if (!strftime(buf, bufsize + 2, tmp, tm)) |
|
1984 { |
|
1985 if (*buf) { |
|
1986 buf[0] = '\0'; |
|
1987 return -1; |
|
1988 } |
|
1989 return 0; |
|
1990 } |
|
1991 decr = strlen(buf); |
|
1992 buf += decr; |
|
1993 bufsize -= decr - 2; |
|
1994 #endif |
|
1995 break; |
|
1996 } |
|
1997 } else { |
|
1998 if (ztrftimebuf(&bufsize, 1)) |
|
1999 return 0; |
|
2000 *buf++ = *fmt++; |
|
2001 } |
|
2002 *buf = '\0'; |
|
2003 return buf - origbuf; |
|
2004 } |
|
2005 |
|
2006 /**/ |
|
2007 mod_export char * |
|
2008 zjoin(char **arr, int delim, int heap) |
|
2009 { |
|
2010 int len = 0; |
|
2011 char **s, *ret, *ptr; |
|
2012 |
|
2013 for (s = arr; *s; s++) |
|
2014 len += strlen(*s) + 1 + (imeta(delim) ? 1 : 0); |
|
2015 if (!len) |
|
2016 return heap? "" : ztrdup(""); |
|
2017 ptr = ret = (heap ? (char *) hcalloc(len) : (char *) zshcalloc(len)); |
|
2018 for (s = arr; *s; s++) { |
|
2019 strucpy(&ptr, *s); |
|
2020 if (imeta(delim)) { |
|
2021 *ptr++ = Meta; |
|
2022 *ptr++ = delim ^ 32; |
|
2023 } |
|
2024 else |
|
2025 *ptr++ = delim; |
|
2026 } |
|
2027 ptr[-1 - (imeta(delim) ? 1 : 0)] = '\0'; |
|
2028 return ret; |
|
2029 } |
|
2030 |
|
2031 /* Split a string containing a colon separated list * |
|
2032 * of items into an array of strings. */ |
|
2033 |
|
2034 /**/ |
|
2035 char ** |
|
2036 colonsplit(char *s, int uniq) |
|
2037 { |
|
2038 int ct; |
|
2039 char *t, **ret, **ptr, **p; |
|
2040 |
|
2041 for (t = s, ct = 0; *t; t++) /* count number of colons */ |
|
2042 if (*t == ':') |
|
2043 ct++; |
|
2044 ptr = ret = (char **) zalloc(sizeof(char **) * (ct + 2)); |
|
2045 |
|
2046 t = s; |
|
2047 do { |
|
2048 s = t; |
|
2049 /* move t to point at next colon */ |
|
2050 for (; *t && *t != ':'; t++); |
|
2051 if (uniq) |
|
2052 for (p = ret; p < ptr; p++) |
|
2053 if ((int)strlen(*p) == t - s && ! strncmp(*p, s, t - s)) |
|
2054 goto cont; |
|
2055 *ptr = (char *) zalloc((t - s) + 1); |
|
2056 ztrncpy(*ptr++, s, t - s); |
|
2057 cont: ; |
|
2058 } |
|
2059 while (*t++); |
|
2060 *ptr = NULL; |
|
2061 return ret; |
|
2062 } |
|
2063 |
|
2064 /**/ |
|
2065 static int |
|
2066 skipwsep(char **s) |
|
2067 { |
|
2068 char *t = *s; |
|
2069 int i = 0; |
|
2070 |
|
2071 while (*t && iwsep(*t == Meta ? t[1] ^ 32 : *t)) { |
|
2072 if (*t == Meta) |
|
2073 t++; |
|
2074 t++; |
|
2075 i++; |
|
2076 } |
|
2077 *s = t; |
|
2078 return i; |
|
2079 } |
|
2080 |
|
2081 /* |
|
2082 * haven't worked out what allownull does; it's passed down from |
|
2083 * sepsplit but all the cases it's used are either 0 or 1 without |
|
2084 * a comment. it seems to be something to do with the `nulstring' |
|
2085 * which i think is some kind of a metafication thing, so probably |
|
2086 * allownull's value is associated with whether we are using |
|
2087 * metafied strings. |
|
2088 * see findsep() below for handling of `quote' argument |
|
2089 */ |
|
2090 |
|
2091 /**/ |
|
2092 mod_export char ** |
|
2093 spacesplit(char *s, int allownull, int heap, int quote) |
|
2094 { |
|
2095 char *t, **ret, **ptr; |
|
2096 int l = sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1); |
|
2097 char *(*dup)(const char *) = (heap ? dupstring : ztrdup); |
|
2098 |
|
2099 ptr = ret = (heap ? (char **) hcalloc(l) : (char **) zshcalloc(l)); |
|
2100 |
|
2101 if (quote) { |
|
2102 /* |
|
2103 * we will be stripping quoted separators by hacking string, |
|
2104 * so make sure it's hackable. |
|
2105 */ |
|
2106 s = dupstring(s); |
|
2107 } |
|
2108 |
|
2109 t = s; |
|
2110 skipwsep(&s); |
|
2111 if (*s && isep(*s == Meta ? s[1] ^ 32 : *s)) |
|
2112 *ptr++ = dup(allownull ? "" : nulstring); |
|
2113 else if (!allownull && t != s) |
|
2114 *ptr++ = dup(""); |
|
2115 while (*s) { |
|
2116 if (isep(*s == Meta ? s[1] ^ 32 : *s) || (quote && *s == '\\')) { |
|
2117 if (*s == Meta) |
|
2118 s++; |
|
2119 s++; |
|
2120 skipwsep(&s); |
|
2121 } |
|
2122 t = s; |
|
2123 findsep(&s, NULL, quote); |
|
2124 if (s > t || allownull) { |
|
2125 *ptr = (heap ? (char *) hcalloc((s - t) + 1) : |
|
2126 (char *) zshcalloc((s - t) + 1)); |
|
2127 ztrncpy(*ptr++, t, s - t); |
|
2128 } else |
|
2129 *ptr++ = dup(nulstring); |
|
2130 t = s; |
|
2131 skipwsep(&s); |
|
2132 } |
|
2133 if (!allownull && t != s) |
|
2134 *ptr++ = dup(""); |
|
2135 *ptr = NULL; |
|
2136 return ret; |
|
2137 } |
|
2138 |
|
2139 /**/ |
|
2140 static int |
|
2141 findsep(char **s, char *sep, int quote) |
|
2142 { |
|
2143 /* |
|
2144 * *s is the string we are looking along, which will be updated |
|
2145 * to the point we have got to. |
|
2146 * |
|
2147 * sep is a possibly multicharacter separator to look for. If NULL, |
|
2148 * use normal separator characters. |
|
2149 * |
|
2150 * quote is a flag that '\<sep>' should not be treated as a separator. |
|
2151 * in this case we need to be able to strip the backslash directly |
|
2152 * in the string, so the calling function must have sent us something |
|
2153 * modifiable. currently this only works for sep == NULL. also in |
|
2154 * in this case only, we need to turn \\ into \. |
|
2155 */ |
|
2156 int i; |
|
2157 char *t, *tt; |
|
2158 |
|
2159 if (!sep) { |
|
2160 for (t = *s; *t; t++) { |
|
2161 if (quote && *t == '\\' && |
|
2162 (isep(t[1] == Meta ? (t[2] ^ 32) : t[1]) || t[1] == '\\')) { |
|
2163 chuck(t); |
|
2164 if (*t == Meta) |
|
2165 t++; |
|
2166 continue; |
|
2167 } |
|
2168 if (*t == Meta) { |
|
2169 if (isep(t[1] ^ 32)) |
|
2170 break; |
|
2171 t++; |
|
2172 } else if (isep(*t)) |
|
2173 break; |
|
2174 } |
|
2175 i = t - *s; |
|
2176 *s = t; |
|
2177 return i; |
|
2178 } |
|
2179 if (!sep[0]) { |
|
2180 if (**s) { |
|
2181 if (**s == Meta) |
|
2182 *s += 2; |
|
2183 else |
|
2184 ++*s; |
|
2185 return 1; |
|
2186 } |
|
2187 return -1; |
|
2188 } |
|
2189 for (i = 0; **s; i++) { |
|
2190 for (t = sep, tt = *s; *t && *tt && *t == *tt; t++, tt++); |
|
2191 if (!*t) |
|
2192 return i; |
|
2193 if (*(*s)++ == Meta) { |
|
2194 #ifdef DEBUG |
|
2195 if (! *(*s)++) |
|
2196 fprintf(stderr, "BUG: unexpected end of string in findsep()\n"); |
|
2197 #else |
|
2198 (*s)++; |
|
2199 #endif |
|
2200 } |
|
2201 } |
|
2202 return -1; |
|
2203 } |
|
2204 |
|
2205 /**/ |
|
2206 char * |
|
2207 findword(char **s, char *sep) |
|
2208 { |
|
2209 char *r, *t; |
|
2210 int sl; |
|
2211 |
|
2212 if (!**s) |
|
2213 return NULL; |
|
2214 |
|
2215 if (sep) { |
|
2216 sl = strlen(sep); |
|
2217 r = *s; |
|
2218 while (! findsep(s, sep, 0)) { |
|
2219 r = *s += sl; |
|
2220 } |
|
2221 return r; |
|
2222 } |
|
2223 for (t = *s; *t; t++) { |
|
2224 if (*t == Meta) { |
|
2225 if (! isep(t[1] ^ 32)) |
|
2226 break; |
|
2227 t++; |
|
2228 } else if (! isep(*t)) |
|
2229 break; |
|
2230 } |
|
2231 *s = t; |
|
2232 findsep(s, sep, 0); |
|
2233 return t; |
|
2234 } |
|
2235 |
|
2236 /**/ |
|
2237 int |
|
2238 wordcount(char *s, char *sep, int mul) |
|
2239 { |
|
2240 int r, sl, c; |
|
2241 |
|
2242 if (sep) { |
|
2243 r = 1; |
|
2244 sl = strlen(sep); |
|
2245 for (; (c = findsep(&s, sep, 0)) >= 0; s += sl) |
|
2246 if ((c && *(s + sl)) || mul) |
|
2247 r++; |
|
2248 } else { |
|
2249 char *t = s; |
|
2250 |
|
2251 r = 0; |
|
2252 if (mul <= 0) |
|
2253 skipwsep(&s); |
|
2254 if ((*s && isep(*s == Meta ? s[1] ^ 32 : *s)) || |
|
2255 (mul < 0 && t != s)) |
|
2256 r++; |
|
2257 for (; *s; r++) { |
|
2258 if (isep(*s == Meta ? s[1] ^ 32 : *s)) { |
|
2259 if (*s == Meta) |
|
2260 s++; |
|
2261 s++; |
|
2262 if (mul <= 0) |
|
2263 skipwsep(&s); |
|
2264 } |
|
2265 findsep(&s, NULL, 0); |
|
2266 t = s; |
|
2267 if (mul <= 0) |
|
2268 skipwsep(&s); |
|
2269 } |
|
2270 if (mul < 0 && t != s) |
|
2271 r++; |
|
2272 } |
|
2273 return r; |
|
2274 } |
|
2275 |
|
2276 /**/ |
|
2277 mod_export char * |
|
2278 sepjoin(char **s, char *sep, int heap) |
|
2279 { |
|
2280 char *r, *p, **t; |
|
2281 int l, sl; |
|
2282 char sepbuf[3]; |
|
2283 |
|
2284 if (!*s) |
|
2285 return heap ? "" : ztrdup(""); |
|
2286 if (!sep) { |
|
2287 p = sep = sepbuf; |
|
2288 if (ifs) { |
|
2289 *p++ = *ifs; |
|
2290 *p++ = *ifs == Meta ? ifs[1] ^ 32 : '\0'; |
|
2291 } else { |
|
2292 *p++ = ' '; |
|
2293 } |
|
2294 *p = '\0'; |
|
2295 } |
|
2296 sl = strlen(sep); |
|
2297 for (t = s, l = 1 - sl; *t; l += strlen(*t) + sl, t++); |
|
2298 r = p = (heap ? (char *) hcalloc(l) : (char *) zshcalloc(l)); |
|
2299 t = s; |
|
2300 while (*t) { |
|
2301 strucpy(&p, *t); |
|
2302 if (*++t) |
|
2303 strucpy(&p, sep); |
|
2304 } |
|
2305 *p = '\0'; |
|
2306 return r; |
|
2307 } |
|
2308 |
|
2309 /**/ |
|
2310 char ** |
|
2311 sepsplit(char *s, char *sep, int allownull, int heap) |
|
2312 { |
|
2313 int n, sl; |
|
2314 char *t, *tt, **r, **p; |
|
2315 |
|
2316 if (!sep) |
|
2317 return spacesplit(s, allownull, heap, 0); |
|
2318 |
|
2319 sl = strlen(sep); |
|
2320 n = wordcount(s, sep, 1); |
|
2321 r = p = (heap ? (char **) hcalloc((n + 1) * sizeof(char *)) : |
|
2322 (char **) zshcalloc((n + 1) * sizeof(char *))); |
|
2323 |
|
2324 for (t = s; n--;) { |
|
2325 tt = t; |
|
2326 findsep(&t, sep, 0); |
|
2327 *p = (heap ? (char *) hcalloc(t - tt + 1) : |
|
2328 (char *) zshcalloc(t - tt + 1)); |
|
2329 strncpy(*p, tt, t - tt); |
|
2330 (*p)[t - tt] = '\0'; |
|
2331 p++; |
|
2332 t += sl; |
|
2333 } |
|
2334 *p = NULL; |
|
2335 |
|
2336 return r; |
|
2337 } |
|
2338 |
|
2339 /* Get the definition of a shell function */ |
|
2340 |
|
2341 /**/ |
|
2342 mod_export Eprog |
|
2343 getshfunc(char *nam) |
|
2344 { |
|
2345 Shfunc shf; |
|
2346 |
|
2347 if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, nam))) |
|
2348 return &dummy_eprog; |
|
2349 |
|
2350 return shf->funcdef; |
|
2351 } |
|
2352 |
|
2353 /**/ |
|
2354 char ** |
|
2355 mkarray(char *s) |
|
2356 { |
|
2357 char **t = (char **) zalloc((s) ? (2 * sizeof s) : (sizeof s)); |
|
2358 |
|
2359 if ((*t = s)) |
|
2360 t[1] = NULL; |
|
2361 return t; |
|
2362 } |
|
2363 |
|
2364 /**/ |
|
2365 mod_export void |
|
2366 zbeep(void) |
|
2367 { |
|
2368 char *vb; |
|
2369 queue_signals(); |
|
2370 if ((vb = getsparam("ZBEEP"))) { |
|
2371 int len; |
|
2372 vb = getkeystring(vb, &len, 2, NULL); |
|
2373 write(SHTTY, vb, len); |
|
2374 } else if (isset(BEEP)) |
|
2375 write(SHTTY, "\07", 1); |
|
2376 unqueue_signals(); |
|
2377 } |
|
2378 |
|
2379 /**/ |
|
2380 mod_export void |
|
2381 freearray(char **s) |
|
2382 { |
|
2383 char **t = s; |
|
2384 |
|
2385 DPUTS(!s, "freearray() with zero argument"); |
|
2386 |
|
2387 while (*s) |
|
2388 zsfree(*s++); |
|
2389 free(t); |
|
2390 } |
|
2391 |
|
2392 /**/ |
|
2393 int |
|
2394 equalsplit(char *s, char **t) |
|
2395 { |
|
2396 for (; *s && *s != '='; s++); |
|
2397 if (*s == '=') { |
|
2398 *s++ = '\0'; |
|
2399 *t = s; |
|
2400 return 1; |
|
2401 } |
|
2402 return 0; |
|
2403 } |
|
2404 |
|
2405 /* the ztypes table */ |
|
2406 |
|
2407 /**/ |
|
2408 mod_export short int typtab[256]; |
|
2409 |
|
2410 /* initialize the ztypes table */ |
|
2411 |
|
2412 /**/ |
|
2413 void |
|
2414 inittyptab(void) |
|
2415 { |
|
2416 int t0; |
|
2417 char *s; |
|
2418 |
|
2419 for (t0 = 0; t0 != 256; t0++) |
|
2420 typtab[t0] = 0; |
|
2421 for (t0 = 0; t0 != 32; t0++) |
|
2422 typtab[t0] = typtab[t0 + 128] = ICNTRL; |
|
2423 typtab[127] = ICNTRL; |
|
2424 for (t0 = '0'; t0 <= '9'; t0++) |
|
2425 typtab[t0] = IDIGIT | IALNUM | IWORD | IIDENT | IUSER; |
|
2426 for (t0 = 'a'; t0 <= 'z'; t0++) |
|
2427 typtab[t0] = typtab[t0 - 'a' + 'A'] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; |
|
2428 for (t0 = 0240; t0 != 0400; t0++) |
|
2429 typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; |
|
2430 typtab['_'] = IIDENT | IUSER; |
|
2431 typtab['-'] = IUSER; |
|
2432 typtab[' '] |= IBLANK | INBLANK; |
|
2433 typtab['\t'] |= IBLANK | INBLANK; |
|
2434 typtab['\n'] |= INBLANK; |
|
2435 typtab['\0'] |= IMETA; |
|
2436 typtab[STOUC(Meta) ] |= IMETA; |
|
2437 typtab[STOUC(Marker)] |= IMETA; |
|
2438 for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++) |
|
2439 typtab[t0] |= ITOK | IMETA; |
|
2440 for (s = ifs ? ifs : DEFAULT_IFS; *s; s++) { |
|
2441 if (inblank(*s)) { |
|
2442 if (s[1] == *s) |
|
2443 s++; |
|
2444 else |
|
2445 typtab[STOUC(*s)] |= IWSEP; |
|
2446 } |
|
2447 typtab[STOUC(*s == Meta ? *++s ^ 32 : *s)] |= ISEP; |
|
2448 } |
|
2449 for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) |
|
2450 typtab[STOUC(*s == Meta ? *++s ^ 32 : *s)] |= IWORD; |
|
2451 for (s = SPECCHARS; *s; s++) |
|
2452 typtab[STOUC(*s)] |= ISPECIAL; |
|
2453 if (isset(BANGHIST) && bangchar && interact && isset(SHINSTDIN)) |
|
2454 typtab[bangchar] |= ISPECIAL; |
|
2455 } |
|
2456 |
|
2457 /**/ |
|
2458 mod_export char ** |
|
2459 arrdup(char **s) |
|
2460 { |
|
2461 char **x, **y; |
|
2462 |
|
2463 y = x = (char **) zhalloc(sizeof(char *) * (arrlen(s) + 1)); |
|
2464 |
|
2465 while ((*x++ = dupstring(*s++))); |
|
2466 |
|
2467 return y; |
|
2468 } |
|
2469 |
|
2470 /**/ |
|
2471 mod_export char ** |
|
2472 zarrdup(char **s) |
|
2473 { |
|
2474 char **x, **y; |
|
2475 |
|
2476 y = x = (char **) zalloc(sizeof(char *) * (arrlen(s) + 1)); |
|
2477 |
|
2478 while ((*x++ = ztrdup(*s++))); |
|
2479 |
|
2480 return y; |
|
2481 } |
|
2482 |
|
2483 /**/ |
|
2484 static char * |
|
2485 spname(char *oldname) |
|
2486 { |
|
2487 char *p, spnameguess[PATH_MAX + 1], spnamebest[PATH_MAX + 1]; |
|
2488 static char newname[PATH_MAX + 1]; |
|
2489 char *new = newname, *old; |
|
2490 int bestdist = 200, thisdist; |
|
2491 |
|
2492 old = oldname; |
|
2493 for (;;) { |
|
2494 while (*old == '/') |
|
2495 *new++ = *old++; |
|
2496 *new = '\0'; |
|
2497 if (*old == '\0') |
|
2498 return newname; |
|
2499 p = spnameguess; |
|
2500 for (; *old != '/' && *old != '\0'; old++) |
|
2501 if (p < spnameguess + PATH_MAX) |
|
2502 *p++ = *old; |
|
2503 *p = '\0'; |
|
2504 if ((thisdist = mindist(newname, spnameguess, spnamebest)) >= 3) { |
|
2505 if (bestdist < 3) { |
|
2506 strcpy(new, spnameguess); |
|
2507 strcat(new, old); |
|
2508 return newname; |
|
2509 } else |
|
2510 return NULL; |
|
2511 } else |
|
2512 bestdist = thisdist; |
|
2513 for (p = spnamebest; (*new = *p++);) |
|
2514 new++; |
|
2515 } |
|
2516 } |
|
2517 |
|
2518 /**/ |
|
2519 static int |
|
2520 mindist(char *dir, char *mindistguess, char *mindistbest) |
|
2521 { |
|
2522 int mindistd, nd; |
|
2523 DIR *dd; |
|
2524 char *fn; |
|
2525 char buf[PATH_MAX]; |
|
2526 |
|
2527 if (dir[0] == '\0') |
|
2528 dir = "."; |
|
2529 mindistd = 100; |
|
2530 sprintf(buf, "%s/%s", dir, mindistguess); |
|
2531 if (access(unmeta(buf), F_OK) == 0) { |
|
2532 strcpy(mindistbest, mindistguess); |
|
2533 return 0; |
|
2534 } |
|
2535 if (!(dd = opendir(unmeta(dir)))) |
|
2536 return mindistd; |
|
2537 while ((fn = zreaddir(dd, 0))) { |
|
2538 nd = spdist(fn, mindistguess, |
|
2539 (int)strlen(mindistguess) / 4 + 1); |
|
2540 if (nd <= mindistd) { |
|
2541 strcpy(mindistbest, fn); |
|
2542 mindistd = nd; |
|
2543 if (mindistd == 0) |
|
2544 break; |
|
2545 } |
|
2546 } |
|
2547 closedir(dd); |
|
2548 return mindistd; |
|
2549 } |
|
2550 |
|
2551 /**/ |
|
2552 static int |
|
2553 spdist(char *s, char *t, int thresh) |
|
2554 { |
|
2555 char *p, *q; |
|
2556 const char qwertykeymap[] = |
|
2557 "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ |
|
2558 \t1234567890-=\t\ |
|
2559 \tqwertyuiop[]\t\ |
|
2560 \tasdfghjkl;'\n\t\ |
|
2561 \tzxcvbnm,./\t\t\t\ |
|
2562 \n\n\n\n\n\n\n\n\n\n\n\n\n\n\ |
|
2563 \t!@#$%^&*()_+\t\ |
|
2564 \tQWERTYUIOP{}\t\ |
|
2565 \tASDFGHJKL:\"\n\t\ |
|
2566 \tZXCVBNM<>?\n\n\t\ |
|
2567 \n\n\n\n\n\n\n\n\n\n\n\n\n\n"; |
|
2568 const char dvorakkeymap[] = |
|
2569 "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ |
|
2570 \t1234567890[]\t\ |
|
2571 \t',.pyfgcrl/=\t\ |
|
2572 \taoeuidhtns-\n\t\ |
|
2573 \t;qjkxbmwvz\t\t\t\ |
|
2574 \n\n\n\n\n\n\n\n\n\n\n\n\n\n\ |
|
2575 \t!@#$%^&*(){}\t\ |
|
2576 \t\"<>PYFGCRL?+\t\ |
|
2577 \tAOEUIDHTNS_\n\t\ |
|
2578 \t:QJKXBMWVZ\n\n\t\ |
|
2579 \n\n\n\n\n\n\n\n\n\n\n\n\n\n"; |
|
2580 const char *keymap; |
|
2581 if ( isset( DVORAK ) ) |
|
2582 keymap = dvorakkeymap; |
|
2583 else |
|
2584 keymap = qwertykeymap; |
|
2585 |
|
2586 if (!strcmp(s, t)) |
|
2587 return 0; |
|
2588 /* any number of upper/lower mistakes allowed (dist = 1) */ |
|
2589 for (p = s, q = t; *p && tulower(*p) == tulower(*q); p++, q++); |
|
2590 if (!*p && !*q) |
|
2591 return 1; |
|
2592 if (!thresh) |
|
2593 return 200; |
|
2594 for (p = s, q = t; *p && *q; p++, q++) |
|
2595 if (*p == *q) |
|
2596 continue; /* don't consider "aa" transposed, ash */ |
|
2597 else if (p[1] == q[0] && q[1] == p[0]) /* transpositions */ |
|
2598 return spdist(p + 2, q + 2, thresh - 1) + 1; |
|
2599 else if (p[1] == q[0]) /* missing letter */ |
|
2600 return spdist(p + 1, q + 0, thresh - 1) + 2; |
|
2601 else if (p[0] == q[1]) /* missing letter */ |
|
2602 return spdist(p + 0, q + 1, thresh - 1) + 2; |
|
2603 else if (*p != *q) |
|
2604 break; |
|
2605 if ((!*p && strlen(q) == 1) || (!*q && strlen(p) == 1)) |
|
2606 return 2; |
|
2607 for (p = s, q = t; *p && *q; p++, q++) |
|
2608 if (p[0] != q[0] && p[1] == q[1]) { |
|
2609 int t0; |
|
2610 char *z; |
|
2611 |
|
2612 /* mistyped letter */ |
|
2613 |
|
2614 if (!(z = strchr(keymap, p[0])) || *z == '\n' || *z == '\t') |
|
2615 return spdist(p + 1, q + 1, thresh - 1) + 1; |
|
2616 t0 = z - keymap; |
|
2617 if (*q == keymap[t0 - 15] || *q == keymap[t0 - 14] || |
|
2618 *q == keymap[t0 - 13] || |
|
2619 *q == keymap[t0 - 1] || *q == keymap[t0 + 1] || |
|
2620 *q == keymap[t0 + 13] || *q == keymap[t0 + 14] || |
|
2621 *q == keymap[t0 + 15]) |
|
2622 return spdist(p + 1, q + 1, thresh - 1) + 2; |
|
2623 return 200; |
|
2624 } else if (*p != *q) |
|
2625 break; |
|
2626 return 200; |
|
2627 } |
|
2628 |
|
2629 /* set cbreak mode, or the equivalent */ |
|
2630 |
|
2631 /**/ |
|
2632 void |
|
2633 setcbreak(void) |
|
2634 { |
|
2635 struct ttyinfo ti; |
|
2636 |
|
2637 ti = shttyinfo; |
|
2638 #ifdef HAS_TIO |
|
2639 ti.tio.c_lflag &= ~ICANON; |
|
2640 ti.tio.c_cc[VMIN] = 1; |
|
2641 ti.tio.c_cc[VTIME] = 0; |
|
2642 #else |
|
2643 ti.sgttyb.sg_flags |= CBREAK; |
|
2644 #endif |
|
2645 settyinfo(&ti); |
|
2646 } |
|
2647 |
|
2648 /* give the tty to some process */ |
|
2649 |
|
2650 /**/ |
|
2651 mod_export void |
|
2652 attachtty(pid_t pgrp) |
|
2653 { |
|
2654 static int ep = 0; |
|
2655 |
|
2656 if (jobbing) { |
|
2657 #ifdef HAVE_TCSETPGRP |
|
2658 if (SHTTY != -1 && tcsetpgrp(SHTTY, pgrp) == -1 && !ep) |
|
2659 #else |
|
2660 # if ardent |
|
2661 if (SHTTY != -1 && setpgrp() == -1 && !ep) |
|
2662 # else |
|
2663 int arg = pgrp; |
|
2664 |
|
2665 if (SHTTY != -1 && ioctl(SHTTY, TIOCSPGRP, &arg) == -1 && !ep) |
|
2666 # endif |
|
2667 #endif |
|
2668 { |
|
2669 if (pgrp != mypgrp && kill(-pgrp, 0) == -1) |
|
2670 attachtty(mypgrp); |
|
2671 else { |
|
2672 if (errno != ENOTTY) |
|
2673 { |
|
2674 zwarn("can't set tty pgrp: %e", NULL, errno); |
|
2675 fflush(stderr); |
|
2676 } |
|
2677 opts[MONITOR] = 0; |
|
2678 ep = 1; |
|
2679 } |
|
2680 } |
|
2681 } |
|
2682 } |
|
2683 |
|
2684 /* get the process group associated with the tty */ |
|
2685 |
|
2686 /**/ |
|
2687 pid_t |
|
2688 gettygrp(void) |
|
2689 { |
|
2690 pid_t arg; |
|
2691 |
|
2692 if (SHTTY == -1) |
|
2693 return -1; |
|
2694 |
|
2695 #ifdef HAVE_TCSETPGRP |
|
2696 arg = tcgetpgrp(SHTTY); |
|
2697 #else |
|
2698 ioctl(SHTTY, TIOCGPGRP, &arg); |
|
2699 #endif |
|
2700 |
|
2701 return arg; |
|
2702 } |
|
2703 |
|
2704 /* Return the output baudrate */ |
|
2705 |
|
2706 #ifdef HAVE_SELECT |
|
2707 /**/ |
|
2708 long |
|
2709 getbaudrate(struct ttyinfo *shttyinfo) |
|
2710 { |
|
2711 long speedcode; |
|
2712 |
|
2713 #ifdef HAS_TIO |
|
2714 # if defined(HAVE_TCGETATTR) && defined(HAVE_TERMIOS_H) |
|
2715 speedcode = cfgetospeed(&shttyinfo->tio); |
|
2716 # else |
|
2717 speedcode = shttyinfo->tio.c_cflag & CBAUD; |
|
2718 # endif |
|
2719 #else |
|
2720 speedcode = shttyinfo->sgttyb.sg_ospeed; |
|
2721 #endif |
|
2722 |
|
2723 switch (speedcode) { |
|
2724 case B0: |
|
2725 return (0L); |
|
2726 case B50: |
|
2727 return (50L); |
|
2728 case B75: |
|
2729 return (75L); |
|
2730 case B110: |
|
2731 return (110L); |
|
2732 case B134: |
|
2733 return (134L); |
|
2734 case B150: |
|
2735 return (150L); |
|
2736 case B200: |
|
2737 return (200L); |
|
2738 case B300: |
|
2739 return (300L); |
|
2740 case B600: |
|
2741 return (600L); |
|
2742 #ifdef _B900 |
|
2743 case _B900: |
|
2744 return (900L); |
|
2745 #endif |
|
2746 case B1200: |
|
2747 return (1200L); |
|
2748 case B1800: |
|
2749 return (1800L); |
|
2750 case B2400: |
|
2751 return (2400L); |
|
2752 #ifdef _B3600 |
|
2753 case _B3600: |
|
2754 return (3600L); |
|
2755 #endif |
|
2756 case B4800: |
|
2757 return (4800L); |
|
2758 #ifdef _B7200 |
|
2759 case _B7200: |
|
2760 return (7200L); |
|
2761 #endif |
|
2762 case B9600: |
|
2763 return (9600L); |
|
2764 #ifdef B19200 |
|
2765 case B19200: |
|
2766 return (19200L); |
|
2767 #else |
|
2768 # ifdef EXTA |
|
2769 case EXTA: |
|
2770 return (19200L); |
|
2771 # endif |
|
2772 #endif |
|
2773 #ifdef B38400 |
|
2774 case B38400: |
|
2775 return (38400L); |
|
2776 #else |
|
2777 # ifdef EXTB |
|
2778 case EXTB: |
|
2779 return (38400L); |
|
2780 # endif |
|
2781 #endif |
|
2782 #ifdef B57600 |
|
2783 case B57600: |
|
2784 return (57600L); |
|
2785 #endif |
|
2786 #ifdef B115200 |
|
2787 case B115200: |
|
2788 return (115200L); |
|
2789 #endif |
|
2790 #ifdef B230400 |
|
2791 case B230400: |
|
2792 return (230400L); |
|
2793 #endif |
|
2794 #ifdef B460800 |
|
2795 case B460800: |
|
2796 return (460800L); |
|
2797 #endif |
|
2798 default: |
|
2799 if (speedcode >= 100) |
|
2800 return speedcode; |
|
2801 break; |
|
2802 } |
|
2803 return (0L); |
|
2804 } |
|
2805 #endif |
|
2806 |
|
2807 /* Escape tokens and null characters. Buf is the string which should be * |
|
2808 * escaped. len is the length of the string. If len is -1, buf should be * |
|
2809 * null terminated. If len is non-negative and the third parameter is not * |
|
2810 * META_DUP, buf should point to an at least len+1 long memory area. The * |
|
2811 * return value points to the quoted string. If the given string does not * |
|
2812 * contain any special character which should be quoted and the third * |
|
2813 * parameter is not META_(HEAP|)DUP, buf is returned unchanged (a * |
|
2814 * terminating null character is appended to buf if necessary). Otherwise * |
|
2815 * the third `heap' argument determines the method used to allocate space * |
|
2816 * for the result. It can have the following values: * |
|
2817 * META_REALLOC: use zrealloc on buf * |
|
2818 * META_HREALLOC: use hrealloc on buf * |
|
2819 * META_USEHEAP: get memory from the heap. This leaves buf unchanged. * |
|
2820 * META_NOALLOC: buf points to a memory area which is long enough to hold * |
|
2821 * the quoted form, just quote it and return buf. * |
|
2822 * META_STATIC: store the quoted string in a static area. The original * |
|
2823 * string should be at most PATH_MAX long. * |
|
2824 * META_ALLOC: allocate memory for the new string with zalloc(). * |
|
2825 * META_DUP: leave buf unchanged and allocate space for the return * |
|
2826 * value even if buf does not contains special characters * |
|
2827 * META_HEAPDUP: same as META_DUP, but uses the heap */ |
|
2828 |
|
2829 /**/ |
|
2830 mod_export char * |
|
2831 metafy(char *buf, int len, int heap) |
|
2832 { |
|
2833 int meta = 0; |
|
2834 char *t, *p, *e; |
|
2835 static char mbuf[PATH_MAX*2+1]; |
|
2836 |
|
2837 if (len == -1) { |
|
2838 for (e = buf, len = 0; *e; len++) |
|
2839 if (imeta(*e++)) |
|
2840 meta++; |
|
2841 } else |
|
2842 for (e = buf; e < buf + len;) |
|
2843 if (imeta(*e++)) |
|
2844 meta++; |
|
2845 |
|
2846 if (meta || heap == META_DUP || heap == META_HEAPDUP) { |
|
2847 switch (heap) { |
|
2848 case META_REALLOC: |
|
2849 buf = zrealloc(buf, len + meta + 1); |
|
2850 break; |
|
2851 case META_HREALLOC: |
|
2852 buf = hrealloc(buf, len, len + meta + 1); |
|
2853 break; |
|
2854 case META_ALLOC: |
|
2855 case META_DUP: |
|
2856 buf = memcpy(zalloc(len + meta + 1), buf, len); |
|
2857 break; |
|
2858 case META_USEHEAP: |
|
2859 case META_HEAPDUP: |
|
2860 buf = memcpy(zhalloc(len + meta + 1), buf, len); |
|
2861 break; |
|
2862 case META_STATIC: |
|
2863 #ifdef DEBUG |
|
2864 if (len > PATH_MAX) { |
|
2865 fprintf(stderr, "BUG: len = %d > PATH_MAX in metafy\n", len); |
|
2866 fflush(stderr); |
|
2867 } |
|
2868 #endif |
|
2869 buf = memcpy(mbuf, buf, len); |
|
2870 break; |
|
2871 #ifdef DEBUG |
|
2872 case META_NOALLOC: |
|
2873 break; |
|
2874 default: |
|
2875 fprintf(stderr, "BUG: metafy called with invalid heap value\n"); |
|
2876 fflush(stderr); |
|
2877 break; |
|
2878 #endif |
|
2879 } |
|
2880 p = buf + len; |
|
2881 e = t = buf + len + meta; |
|
2882 while (meta) { |
|
2883 if (imeta(*--t = *--p)) { |
|
2884 *t-- ^= 32; |
|
2885 *t = Meta; |
|
2886 meta--; |
|
2887 } |
|
2888 } |
|
2889 } |
|
2890 *e = '\0'; |
|
2891 return buf; |
|
2892 } |
|
2893 |
|
2894 /**/ |
|
2895 mod_export char * |
|
2896 unmetafy(char *s, int *len) |
|
2897 { |
|
2898 char *p, *t; |
|
2899 |
|
2900 for (p = s; *p && *p != Meta; p++); |
|
2901 for (t = p; (*t = *p++);) |
|
2902 if (*t++ == Meta) |
|
2903 t[-1] = *p++ ^ 32; |
|
2904 if (len) |
|
2905 *len = t - s; |
|
2906 return s; |
|
2907 } |
|
2908 |
|
2909 /* Return the character length of a metafied substring, given the * |
|
2910 * unmetafied substring length. */ |
|
2911 |
|
2912 /**/ |
|
2913 mod_export int |
|
2914 metalen(const char *s, int len) |
|
2915 { |
|
2916 int mlen = len; |
|
2917 |
|
2918 while (len--) { |
|
2919 if (*s++ == Meta) { |
|
2920 mlen++; |
|
2921 s++; |
|
2922 } |
|
2923 } |
|
2924 return mlen; |
|
2925 } |
|
2926 |
|
2927 /* This function converts a zsh internal string to a form which can be * |
|
2928 * passed to a system call as a filename. The result is stored in a * |
|
2929 * single static area. NULL returned if the result is longer than * |
|
2930 * 4 * PATH_MAX. */ |
|
2931 |
|
2932 /**/ |
|
2933 mod_export char * |
|
2934 unmeta(const char *file_name) |
|
2935 { |
|
2936 static char fn[4 * PATH_MAX]; |
|
2937 char *p; |
|
2938 const char *t; |
|
2939 |
|
2940 for (t = file_name, p = fn; *t && p < fn + 4 * PATH_MAX - 1; p++) |
|
2941 if ((*p = *t++) == Meta) |
|
2942 *p = *t++ ^ 32; |
|
2943 if (*t) |
|
2944 return NULL; |
|
2945 if (p - fn == t - file_name) |
|
2946 return (char *) file_name; |
|
2947 *p = '\0'; |
|
2948 return fn; |
|
2949 } |
|
2950 |
|
2951 /* Unmetafy and compare two strings, with unsigned characters. * |
|
2952 * "a\0" sorts after "a". */ |
|
2953 |
|
2954 /**/ |
|
2955 int |
|
2956 ztrcmp(unsigned char const *s1, unsigned char const *s2) |
|
2957 { |
|
2958 int c1, c2; |
|
2959 |
|
2960 while(*s1 && *s1 == *s2) { |
|
2961 s1++; |
|
2962 s2++; |
|
2963 } |
|
2964 |
|
2965 if(!(c1 = *s1)) |
|
2966 c1 = -1; |
|
2967 else if(c1 == STOUC(Meta)) |
|
2968 c1 = *++s1 ^ 32; |
|
2969 if(!(c2 = *s2)) |
|
2970 c2 = -1; |
|
2971 else if(c2 == STOUC(Meta)) |
|
2972 c2 = *++s2 ^ 32; |
|
2973 |
|
2974 if(c1 == c2) |
|
2975 return 0; |
|
2976 else if(c1 < c2) |
|
2977 return -1; |
|
2978 else |
|
2979 return 1; |
|
2980 } |
|
2981 |
|
2982 /* Return zero if the metafied string s and the non-metafied, * |
|
2983 * len-long string r are the same. Return -1 if r is a prefix * |
|
2984 * of s. Return 1 if r is the lowercase version of s. Return * |
|
2985 * 2 is r is the lowercase prefix of s and return 3 otherwise. */ |
|
2986 |
|
2987 /**/ |
|
2988 mod_export int |
|
2989 metadiffer(char const *s, char const *r, int len) |
|
2990 { |
|
2991 int l = len; |
|
2992 |
|
2993 while (l-- && *s && *r++ == (*s == Meta ? *++s ^ 32 : *s)) |
|
2994 s++; |
|
2995 if (*s && l < 0) |
|
2996 return -1; |
|
2997 if (l < 0) |
|
2998 return 0; |
|
2999 if (!*s) |
|
3000 return 3; |
|
3001 s -= len - l - 1; |
|
3002 r -= len - l; |
|
3003 while (len-- && *s && *r++ == tulower(*s == Meta ? *++s ^ 32 : *s)) |
|
3004 s++; |
|
3005 if (*s && len < 0) |
|
3006 return 2; |
|
3007 if (len < 0) |
|
3008 return 1; |
|
3009 return 3; |
|
3010 } |
|
3011 |
|
3012 /* Return the unmetafied length of a metafied string. */ |
|
3013 |
|
3014 /**/ |
|
3015 mod_export int |
|
3016 ztrlen(char const *s) |
|
3017 { |
|
3018 int l; |
|
3019 |
|
3020 for (l = 0; *s; l++) |
|
3021 if (*s++ == Meta) { |
|
3022 #ifdef DEBUG |
|
3023 if (! *s) |
|
3024 fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n"); |
|
3025 else |
|
3026 #endif |
|
3027 s++; |
|
3028 } |
|
3029 return l; |
|
3030 } |
|
3031 |
|
3032 /* Subtract two pointers in a metafied string. */ |
|
3033 |
|
3034 /**/ |
|
3035 mod_export int |
|
3036 ztrsub(char const *t, char const *s) |
|
3037 { |
|
3038 int l = t - s; |
|
3039 |
|
3040 while (s != t) |
|
3041 if (*s++ == Meta) { |
|
3042 #ifdef DEBUG |
|
3043 if (! *s || s == t) |
|
3044 fprintf(stderr, "BUG: substring ends in the middle of a metachar in ztrsub()\n"); |
|
3045 else |
|
3046 #endif |
|
3047 s++; |
|
3048 l--; |
|
3049 } |
|
3050 return l; |
|
3051 } |
|
3052 |
|
3053 /**/ |
|
3054 mod_export char * |
|
3055 zreaddir(DIR *dir, int ignoredots) |
|
3056 { |
|
3057 struct dirent *de; |
|
3058 |
|
3059 do { |
|
3060 de = readdir(dir); |
|
3061 if(!de) |
|
3062 return NULL; |
|
3063 } while(ignoredots && de->d_name[0] == '.' && |
|
3064 (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))); |
|
3065 |
|
3066 return metafy(de->d_name, -1, META_STATIC); |
|
3067 } |
|
3068 |
|
3069 /* Unmetafy and output a string. Tokens are skipped. */ |
|
3070 |
|
3071 /**/ |
|
3072 mod_export int |
|
3073 zputs(char const *s, FILE *stream) |
|
3074 { |
|
3075 int c; |
|
3076 |
|
3077 while (*s) { |
|
3078 if (*s == Meta) |
|
3079 c = *++s ^ 32; |
|
3080 else if(itok(*s)) { |
|
3081 s++; |
|
3082 continue; |
|
3083 } else |
|
3084 c = *s; |
|
3085 s++; |
|
3086 if (fputc(c, stream) < 0) |
|
3087 return EOF; |
|
3088 } |
|
3089 return 0; |
|
3090 } |
|
3091 |
|
3092 /* Create a visibly-represented duplicate of a string. */ |
|
3093 |
|
3094 /**/ |
|
3095 static char * |
|
3096 nicedup(char const *s, int heap) |
|
3097 { |
|
3098 int c, len = strlen(s) * 5; |
|
3099 VARARR(char, buf, len); |
|
3100 char *p = buf, *n; |
|
3101 |
|
3102 while ((c = *s++)) { |
|
3103 if (itok(c)) { |
|
3104 if (c <= Comma) |
|
3105 c = ztokens[c - Pound]; |
|
3106 else |
|
3107 continue; |
|
3108 } |
|
3109 if (c == Meta) |
|
3110 c = *s++ ^ 32; |
|
3111 n = nicechar(c); |
|
3112 while(*n) |
|
3113 *p++ = *n++; |
|
3114 } |
|
3115 return metafy(buf, p - buf, (heap ? META_HEAPDUP : META_DUP)); |
|
3116 } |
|
3117 |
|
3118 /**/ |
|
3119 mod_export char * |
|
3120 niceztrdup(char const *s) |
|
3121 { |
|
3122 return nicedup(s, 0); |
|
3123 } |
|
3124 |
|
3125 /**/ |
|
3126 mod_export char * |
|
3127 nicedupstring(char const *s) |
|
3128 { |
|
3129 return nicedup(s, 1); |
|
3130 } |
|
3131 |
|
3132 /* Unmetafy and output a string, displaying special characters readably. */ |
|
3133 |
|
3134 /**/ |
|
3135 mod_export int |
|
3136 nicezputs(char const *s, FILE *stream) |
|
3137 { |
|
3138 int c; |
|
3139 |
|
3140 while ((c = *s++)) { |
|
3141 if (itok(c)) { |
|
3142 if (c <= Comma) |
|
3143 c = ztokens[c - Pound]; |
|
3144 else |
|
3145 continue; |
|
3146 } |
|
3147 if (c == Meta) |
|
3148 c = *s++ ^ 32; |
|
3149 if(fputs(nicechar(c), stream) < 0) |
|
3150 return EOF; |
|
3151 } |
|
3152 return 0; |
|
3153 } |
|
3154 |
|
3155 /* Return the length of the visible representation of a metafied string. */ |
|
3156 |
|
3157 /**/ |
|
3158 mod_export size_t |
|
3159 niceztrlen(char const *s) |
|
3160 { |
|
3161 size_t l = 0; |
|
3162 int c; |
|
3163 |
|
3164 while ((c = *s++)) { |
|
3165 if (itok(c)) { |
|
3166 if (c <= Comma) |
|
3167 c = ztokens[c - Pound]; |
|
3168 else |
|
3169 continue; |
|
3170 } |
|
3171 if (c == Meta) |
|
3172 c = *s++ ^ 32; |
|
3173 l += strlen(nicechar(STOUC(c))); |
|
3174 } |
|
3175 return l; |
|
3176 } |
|
3177 |
|
3178 /* check for special characters in the string */ |
|
3179 |
|
3180 /**/ |
|
3181 mod_export int |
|
3182 hasspecial(char const *s) |
|
3183 { |
|
3184 for (; *s; s++) |
|
3185 if (ispecial(*s == Meta ? *++s ^ 32 : *s)) |
|
3186 return 1; |
|
3187 return 0; |
|
3188 } |
|
3189 |
|
3190 /* Quote the string s and return the result. If e is non-zero, the * |
|
3191 * pointer it points to may point to a position in s and in e the position * |
|
3192 * of the corresponding character in the quoted string is returned. * |
|
3193 * The last argument should be zero if this is to be used outside a string, * |
|
3194 * one if it is to be quoted for the inside of a single quoted string, * |
|
3195 * two if it is for the inside of a double quoted string, and * |
|
3196 * three if it is for the inside of a posix quoted string. * |
|
3197 * The string may be metafied and contain tokens. */ |
|
3198 |
|
3199 /**/ |
|
3200 mod_export char * |
|
3201 bslashquote(const char *s, char **e, int instring) |
|
3202 { |
|
3203 const char *u, *tt; |
|
3204 char *v; |
|
3205 char *buf = hcalloc(4 * strlen(s) + 1); |
|
3206 int sf = 0; |
|
3207 |
|
3208 tt = v = buf; |
|
3209 u = s; |
|
3210 for (; *u; u++) { |
|
3211 if (e && *e == u) |
|
3212 *e = v, sf = 1; |
|
3213 if (instring == 3) { |
|
3214 int c = *u; |
|
3215 if (c == Meta) { |
|
3216 c = *++u ^ 32; |
|
3217 } |
|
3218 c &= 0xff; |
|
3219 if(isprint(c)) { |
|
3220 switch (c) { |
|
3221 case '\\': |
|
3222 case '\'': |
|
3223 *v++ = '\\'; |
|
3224 *v++ = c; |
|
3225 break; |
|
3226 |
|
3227 default: |
|
3228 if(imeta(c)) { |
|
3229 *v++ = Meta; |
|
3230 *v++ = c ^ 32; |
|
3231 } |
|
3232 else { |
|
3233 if (isset(BANGHIST) && c == bangchar) { |
|
3234 *v++ = '\\'; |
|
3235 } |
|
3236 *v++ = c; |
|
3237 } |
|
3238 break; |
|
3239 } |
|
3240 } |
|
3241 else { |
|
3242 switch (c) { |
|
3243 case '\0': |
|
3244 *v++ = '\\'; |
|
3245 *v++ = '0'; |
|
3246 if ('0' <= u[1] && u[1] <= '7') { |
|
3247 *v++ = '0'; |
|
3248 *v++ = '0'; |
|
3249 } |
|
3250 break; |
|
3251 |
|
3252 case '\007': *v++ = '\\'; *v++ = 'a'; break; |
|
3253 case '\b': *v++ = '\\'; *v++ = 'b'; break; |
|
3254 case '\f': *v++ = '\\'; *v++ = 'f'; break; |
|
3255 case '\n': *v++ = '\\'; *v++ = 'n'; break; |
|
3256 case '\r': *v++ = '\\'; *v++ = 'r'; break; |
|
3257 case '\t': *v++ = '\\'; *v++ = 't'; break; |
|
3258 case '\v': *v++ = '\\'; *v++ = 'v'; break; |
|
3259 |
|
3260 default: |
|
3261 *v++ = '\\'; |
|
3262 *v++ = '0' + ((c >> 6) & 7); |
|
3263 *v++ = '0' + ((c >> 3) & 7); |
|
3264 *v++ = '0' + (c & 7); |
|
3265 break; |
|
3266 } |
|
3267 } |
|
3268 continue; |
|
3269 } |
|
3270 else if (*u == Tick || *u == Qtick) { |
|
3271 char c = *u++; |
|
3272 |
|
3273 *v++ = c; |
|
3274 while (*u && *u != c) |
|
3275 *v++ = *u++; |
|
3276 *v++ = c; |
|
3277 if (!*u) |
|
3278 u--; |
|
3279 continue; |
|
3280 } |
|
3281 else if ((*u == String || *u == Qstring) && |
|
3282 (u[1] == Inpar || u[1] == Inbrack || u[1] == Inbrace)) { |
|
3283 char c = (u[1] == Inpar ? Outpar : (u[1] == Inbrace ? |
|
3284 Outbrace : Outbrack)); |
|
3285 char beg = *u; |
|
3286 int level = 0; |
|
3287 |
|
3288 *v++ = *u++; |
|
3289 *v++ = *u++; |
|
3290 while (*u && (*u != c || level)) { |
|
3291 if (*u == beg) |
|
3292 level++; |
|
3293 else if (*u == c) |
|
3294 level--; |
|
3295 *v++ = *u++; |
|
3296 } |
|
3297 if (*u) |
|
3298 *v++ = *u; |
|
3299 else |
|
3300 u--; |
|
3301 continue; |
|
3302 } |
|
3303 else if (ispecial(*u) && |
|
3304 ((*u != '=' && *u != '~') || |
|
3305 u == s || |
|
3306 (isset(MAGICEQUALSUBST) && (u[-1] == '=' || u[-1] == ':')) || |
|
3307 (*u == '~' && isset(EXTENDEDGLOB))) && |
|
3308 (!instring || |
|
3309 (isset(BANGHIST) && *u == (char)bangchar && instring != 1) || |
|
3310 (instring == 2 && |
|
3311 (*u == '$' || *u == '`' || *u == '\"' || *u == '\\')) || |
|
3312 (instring == 1 && *u == '\''))) { |
|
3313 if (*u == '\n' || (instring == 1 && *u == '\'')) { |
|
3314 if (unset(RCQUOTES)) { |
|
3315 *v++ = '\''; |
|
3316 if (*u == '\'') |
|
3317 *v++ = '\\'; |
|
3318 *v++ = *u; |
|
3319 *v++ = '\''; |
|
3320 } else if (*u == '\n') |
|
3321 *v++ = '"', *v++ = '\n', *v++ = '"'; |
|
3322 else |
|
3323 *v++ = '\'', *v++ = '\''; |
|
3324 continue; |
|
3325 } else |
|
3326 *v++ = '\\'; |
|
3327 } |
|
3328 if(*u == Meta) |
|
3329 *v++ = *u++; |
|
3330 *v++ = *u; |
|
3331 } |
|
3332 *v = '\0'; |
|
3333 |
|
3334 if (e && *e == u) |
|
3335 *e = v, sf = 1; |
|
3336 DPUTS(e && !sf, "BUG: Wild pointer *e in bslashquote()"); |
|
3337 |
|
3338 return buf; |
|
3339 } |
|
3340 |
|
3341 /* Unmetafy and output a string, quoted if it contains special characters. */ |
|
3342 |
|
3343 /**/ |
|
3344 mod_export int |
|
3345 quotedzputs(char const *s, FILE *stream) |
|
3346 { |
|
3347 int inquote = 0, c; |
|
3348 |
|
3349 /* check for empty string */ |
|
3350 if(!*s) |
|
3351 return fputs("''", stream); |
|
3352 |
|
3353 if (!hasspecial(s)) |
|
3354 return zputs(s, stream); |
|
3355 |
|
3356 if (isset(RCQUOTES)) { |
|
3357 /* use rc-style quotes-within-quotes for the whole string */ |
|
3358 if(fputc('\'', stream) < 0) |
|
3359 return EOF; |
|
3360 while(*s) { |
|
3361 if (*s == Meta) |
|
3362 c = *++s ^ 32; |
|
3363 else |
|
3364 c = *s; |
|
3365 s++; |
|
3366 if (c == '\'') { |
|
3367 if(fputc('\'', stream) < 0) |
|
3368 return EOF; |
|
3369 } else if(c == '\n' && isset(CSHJUNKIEQUOTES)) { |
|
3370 if(fputc('\\', stream) < 0) |
|
3371 return EOF; |
|
3372 } |
|
3373 if(fputc(c, stream) < 0) |
|
3374 return EOF; |
|
3375 } |
|
3376 if(fputc('\'', stream) < 0) |
|
3377 return EOF; |
|
3378 } else { |
|
3379 /* use Bourne-style quoting, avoiding empty quoted strings */ |
|
3380 while(*s) { |
|
3381 if (*s == Meta) |
|
3382 c = *++s ^ 32; |
|
3383 else |
|
3384 c = *s; |
|
3385 s++; |
|
3386 if (c == '\'') { |
|
3387 if(inquote) { |
|
3388 if(fputc('\'', stream) < 0) |
|
3389 return EOF; |
|
3390 inquote=0; |
|
3391 } |
|
3392 if(fputs("\\'", stream) < 0) |
|
3393 return EOF; |
|
3394 } else { |
|
3395 if (!inquote) { |
|
3396 if(fputc('\'', stream) < 0) |
|
3397 return EOF; |
|
3398 inquote=1; |
|
3399 } |
|
3400 if(c == '\n' && isset(CSHJUNKIEQUOTES)) { |
|
3401 if(fputc('\\', stream) < 0) |
|
3402 return EOF; |
|
3403 } |
|
3404 if(fputc(c, stream) < 0) |
|
3405 return EOF; |
|
3406 } |
|
3407 } |
|
3408 if (inquote) { |
|
3409 if(fputc('\'', stream) < 0) |
|
3410 return EOF; |
|
3411 } |
|
3412 } |
|
3413 return 0; |
|
3414 } |
|
3415 |
|
3416 /* Double-quote a metafied string. */ |
|
3417 |
|
3418 /**/ |
|
3419 mod_export char * |
|
3420 dquotedztrdup(char const *s) |
|
3421 { |
|
3422 int len = strlen(s) * 4 + 2; |
|
3423 char *buf = zalloc(len); |
|
3424 char *p = buf, *ret; |
|
3425 |
|
3426 if(isset(CSHJUNKIEQUOTES)) { |
|
3427 int inquote = 0; |
|
3428 |
|
3429 while(*s) { |
|
3430 int c = *s++; |
|
3431 |
|
3432 if (c == Meta) |
|
3433 c = *s++ ^ 32; |
|
3434 switch(c) { |
|
3435 case '"': |
|
3436 case '$': |
|
3437 case '`': |
|
3438 if(inquote) { |
|
3439 *p++ = '"'; |
|
3440 inquote = 0; |
|
3441 } |
|
3442 *p++ = '\\'; |
|
3443 *p++ = c; |
|
3444 break; |
|
3445 default: |
|
3446 if(!inquote) { |
|
3447 *p++ = '"'; |
|
3448 inquote = 1; |
|
3449 } |
|
3450 if(c == '\n') |
|
3451 *p++ = '\\'; |
|
3452 *p++ = c; |
|
3453 break; |
|
3454 } |
|
3455 } |
|
3456 if (inquote) |
|
3457 *p++ = '"'; |
|
3458 } else { |
|
3459 int pending = 0; |
|
3460 |
|
3461 *p++ = '"'; |
|
3462 while(*s) { |
|
3463 int c = *s++; |
|
3464 |
|
3465 if (c == Meta) |
|
3466 c = *s++ ^ 32; |
|
3467 switch(c) { |
|
3468 case '\\': |
|
3469 if(pending) |
|
3470 *p++ = '\\'; |
|
3471 *p++ = '\\'; |
|
3472 pending = 1; |
|
3473 break; |
|
3474 case '"': |
|
3475 case '$': |
|
3476 case '`': |
|
3477 if(pending) |
|
3478 *p++ = '\\'; |
|
3479 *p++ = '\\'; |
|
3480 /* fall through */ |
|
3481 default: |
|
3482 *p++ = c; |
|
3483 pending = 0; |
|
3484 break; |
|
3485 } |
|
3486 } |
|
3487 if(pending) |
|
3488 *p++ = '\\'; |
|
3489 *p++ = '"'; |
|
3490 } |
|
3491 ret = metafy(buf, p - buf, META_DUP); |
|
3492 zfree(buf, len); |
|
3493 return ret; |
|
3494 } |
|
3495 |
|
3496 /* Unmetafy and output a string, double quoting it in its entirety. */ |
|
3497 |
|
3498 #if 0 /**/ |
|
3499 int |
|
3500 dquotedzputs(char const *s, FILE *stream) |
|
3501 { |
|
3502 char *d = dquotedztrdup(s); |
|
3503 int ret = zputs(d, stream); |
|
3504 |
|
3505 zsfree(d); |
|
3506 return ret; |
|
3507 } |
|
3508 #endif |
|
3509 |
|
3510 # if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__) |
|
3511 /* Convert a character from UCS4 encoding to UTF-8 */ |
|
3512 |
|
3513 /**/ |
|
3514 size_t |
|
3515 ucs4toutf8(char *dest, unsigned int wval) |
|
3516 { |
|
3517 size_t len; |
|
3518 |
|
3519 if (wval < 0x80) |
|
3520 len = 1; |
|
3521 else if (wval < 0x800) |
|
3522 len = 2; |
|
3523 else if (wval < 0x10000) |
|
3524 len = 3; |
|
3525 else if (wval < 0x200000) |
|
3526 len = 4; |
|
3527 else if (wval < 0x4000000) |
|
3528 len = 5; |
|
3529 else |
|
3530 len = 6; |
|
3531 |
|
3532 switch (len) { /* falls through except to the last case */ |
|
3533 case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6; |
|
3534 case 5: dest[4] = (wval & 0x3f) | 0x80; wval >>= 6; |
|
3535 case 4: dest[3] = (wval & 0x3f) | 0x80; wval >>= 6; |
|
3536 case 3: dest[2] = (wval & 0x3f) | 0x80; wval >>= 6; |
|
3537 case 2: dest[1] = (wval & 0x3f) | 0x80; wval >>= 6; |
|
3538 *dest = wval | ((0xfc << (6 - len)) & 0xfc); |
|
3539 break; |
|
3540 case 1: *dest = wval; |
|
3541 } |
|
3542 |
|
3543 return len; |
|
3544 } |
|
3545 #endif |
|
3546 |
|
3547 /* |
|
3548 * Decode a key string, turning it into the literal characters. |
|
3549 * The length is returned in len. |
|
3550 * fromwhere determines how the processing works. |
|
3551 * 0: Don't handle keystring, just print-like escapes. |
|
3552 * Expects misc to be present. |
|
3553 * 1: Handle Emacs-like \C-X arguments etc., but not ^X |
|
3554 * Expects misc to be present. |
|
3555 * 2: Handle ^X as well as emacs-like keys; don't handle \c |
|
3556 * for no newlines. |
|
3557 * 3: As 1, but don't handle \c. |
|
3558 * 4: Do $'...' quoting. Overwrites the existing string instead of |
|
3559 * zhalloc'ing. If \uNNNN ever generates multi-byte chars longer |
|
3560 * than 6 bytes, will need to adjust this to re-allocate memory. |
|
3561 * 5: As 2, but \- is special. Expects misc to be defined. |
|
3562 * 6: As 2, but parses only one character and returns end-pointer |
|
3563 * and parsed character in *misc |
|
3564 */ |
|
3565 |
|
3566 /**/ |
|
3567 mod_export char * |
|
3568 getkeystring(char *s, int *len, int fromwhere, int *misc) |
|
3569 { |
|
3570 char *buf, tmp[1]; |
|
3571 char *t, *u = NULL; |
|
3572 char svchar = '\0'; |
|
3573 int meta = 0, control = 0; |
|
3574 int i; |
|
3575 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) |
|
3576 wint_t wval; |
|
3577 size_t count; |
|
3578 #else |
|
3579 unsigned int wval; |
|
3580 # if defined(HAVE_NL_LANGINFO) && defined(CODESET) && defined(HAVE_ICONV) |
|
3581 iconv_t cd; |
|
3582 char inbuf[4]; |
|
3583 size_t inbytes, outbytes; |
|
3584 size_t count; |
|
3585 # endif |
|
3586 #endif |
|
3587 |
|
3588 if (fromwhere == 6) |
|
3589 t = buf = tmp; |
|
3590 else if (fromwhere != 4) |
|
3591 t = buf = zhalloc(strlen(s) + 1); |
|
3592 else { |
|
3593 t = buf = s; |
|
3594 s += 2; |
|
3595 } |
|
3596 for (; *s; s++) { |
|
3597 if (*s == '\\' && s[1]) { |
|
3598 switch (*++s) { |
|
3599 case 'a': |
|
3600 #ifdef __STDC__ |
|
3601 *t++ = '\a'; |
|
3602 #else |
|
3603 *t++ = '\07'; |
|
3604 #endif |
|
3605 break; |
|
3606 case 'n': |
|
3607 *t++ = '\n'; |
|
3608 break; |
|
3609 case 'b': |
|
3610 *t++ = '\b'; |
|
3611 break; |
|
3612 case 't': |
|
3613 *t++ = '\t'; |
|
3614 break; |
|
3615 case 'v': |
|
3616 *t++ = '\v'; |
|
3617 break; |
|
3618 case 'f': |
|
3619 *t++ = '\f'; |
|
3620 break; |
|
3621 case 'r': |
|
3622 *t++ = '\r'; |
|
3623 break; |
|
3624 case 'E': |
|
3625 if (!fromwhere) { |
|
3626 *t++ = '\\', s--; |
|
3627 continue; |
|
3628 } |
|
3629 case 'e': |
|
3630 *t++ = '\033'; |
|
3631 break; |
|
3632 case 'M': |
|
3633 if (fromwhere) { |
|
3634 if (s[1] == '-') |
|
3635 s++; |
|
3636 meta = 1 + control; /* preserve the order of ^ and meta */ |
|
3637 } else |
|
3638 *t++ = '\\', s--; |
|
3639 continue; |
|
3640 case 'C': |
|
3641 if (fromwhere) { |
|
3642 if (s[1] == '-') |
|
3643 s++; |
|
3644 control = 1; |
|
3645 } else |
|
3646 *t++ = '\\', s--; |
|
3647 continue; |
|
3648 case Meta: |
|
3649 *t++ = '\\', s--; |
|
3650 break; |
|
3651 case '-': |
|
3652 if (fromwhere == 5) { |
|
3653 *misc = 1; |
|
3654 break; |
|
3655 } |
|
3656 goto def; |
|
3657 case 'c': |
|
3658 if (fromwhere < 2) { |
|
3659 *misc = 1; |
|
3660 break; |
|
3661 } |
|
3662 case 'u': |
|
3663 case 'U': |
|
3664 wval = 0; |
|
3665 for (i=(*s == 'u' ? 4 : 8); i>0; i--) { |
|
3666 if (*++s && idigit(*s)) |
|
3667 wval = wval * 16 + (*s - '0'); |
|
3668 else if (*s && ((*s >= 'a' && *s <= 'f') || |
|
3669 (*s >= 'A' && *s <= 'F'))) |
|
3670 wval = wval * 16 + (*s & 0x1f) + 9; |
|
3671 else { |
|
3672 s--; |
|
3673 break; |
|
3674 } |
|
3675 } |
|
3676 if (fromwhere == 6) { |
|
3677 *misc = wval; |
|
3678 return s+1; |
|
3679 } |
|
3680 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) |
|
3681 count = wctomb(t, (wchar_t)wval); |
|
3682 if (count == (size_t)-1) { |
|
3683 zerr("character not in range", NULL, 0); |
|
3684 if (fromwhere == 4) { |
|
3685 for (u = t; (*u++ = *++s);); |
|
3686 return t; |
|
3687 } |
|
3688 *t = '\0'; |
|
3689 *len = t - buf; |
|
3690 return buf; |
|
3691 } |
|
3692 t += count; |
|
3693 continue; |
|
3694 # else |
|
3695 # if defined(HAVE_NL_LANGINFO) && defined(CODESET) |
|
3696 if (!strcmp(nl_langinfo(CODESET), "UTF-8")) { |
|
3697 t += ucs4toutf8(t, wval); |
|
3698 continue; |
|
3699 } else { |
|
3700 # ifdef HAVE_ICONV |
|
3701 ICONV_CONST char *inptr = inbuf; |
|
3702 inbytes = 4; |
|
3703 outbytes = 6; |
|
3704 /* store value in big endian form */ |
|
3705 for (i=3;i>=0;i--) { |
|
3706 inbuf[i] = wval & 0xff; |
|
3707 wval >>= 8; |
|
3708 } |
|
3709 |
|
3710 cd = iconv_open(nl_langinfo(CODESET), "UCS-4BE"); |
|
3711 if (cd == (iconv_t)-1) { |
|
3712 zerr("cannot do charset conversion", NULL, 0); |
|
3713 if (fromwhere == 4) { |
|
3714 for (u = t; (*u++ = *++s);); |
|
3715 return t; |
|
3716 } |
|
3717 *t = '\0'; |
|
3718 *len = t - buf; |
|
3719 return buf; |
|
3720 } |
|
3721 count = iconv(cd, (const char**)&inptr, &inbytes, &t, &outbytes); |
|
3722 iconv_close(cd); |
|
3723 if (count == (size_t)-1) { |
|
3724 zerr("character not in range", NULL, 0); |
|
3725 *t = '\0'; |
|
3726 *len = t - buf; |
|
3727 return buf; |
|
3728 } |
|
3729 continue; |
|
3730 # else |
|
3731 zerr("cannot do charset conversion", NULL, 0); |
|
3732 *t = '\0'; |
|
3733 *len = t - buf; |
|
3734 return buf; |
|
3735 # endif |
|
3736 } |
|
3737 # else |
|
3738 zerr("cannot do charset conversion", NULL, 0); |
|
3739 *t = '\0'; |
|
3740 *len = t - buf; |
|
3741 return buf; |
|
3742 # endif |
|
3743 # endif |
|
3744 default: |
|
3745 def: |
|
3746 if ((idigit(*s) && *s < '8') || *s == 'x') { |
|
3747 if (!fromwhere) { |
|
3748 if (*s == '0') |
|
3749 s++; |
|
3750 else if (*s != 'x') { |
|
3751 *t++ = '\\', s--; |
|
3752 continue; |
|
3753 } |
|
3754 } |
|
3755 if (s[1] && s[2] && s[3]) { |
|
3756 svchar = s[3]; |
|
3757 s[3] = '\0'; |
|
3758 u = s; |
|
3759 } |
|
3760 *t++ = zstrtol(s + (*s == 'x'), &s, |
|
3761 (*s == 'x') ? 16 : 8); |
|
3762 if (svchar) { |
|
3763 u[3] = svchar; |
|
3764 svchar = '\0'; |
|
3765 } |
|
3766 s--; |
|
3767 } else { |
|
3768 if (!fromwhere && *s != '\\') |
|
3769 *t++ = '\\'; |
|
3770 *t++ = *s; |
|
3771 } |
|
3772 break; |
|
3773 } |
|
3774 } else if (fromwhere == 4 && *s == Snull) { |
|
3775 for (u = t; (*u++ = *s++);); |
|
3776 return t + 1; |
|
3777 } else if (*s == '^' && !control && |
|
3778 (fromwhere == 2 || fromwhere == 5 || fromwhere == 6)) { |
|
3779 control = 1; |
|
3780 continue; |
|
3781 } else if (*s == Meta) |
|
3782 *t++ = *++s ^ 32; |
|
3783 else |
|
3784 *t++ = *s; |
|
3785 if (meta == 2) { |
|
3786 t[-1] |= 0x80; |
|
3787 meta = 0; |
|
3788 } |
|
3789 if (control) { |
|
3790 if (t[-1] == '?') |
|
3791 t[-1] = 0x7f; |
|
3792 else |
|
3793 t[-1] &= 0x9f; |
|
3794 control = 0; |
|
3795 } |
|
3796 if (meta) { |
|
3797 t[-1] |= 0x80; |
|
3798 meta = 0; |
|
3799 } |
|
3800 if (fromwhere == 4 && imeta(t[-1])) { |
|
3801 *t = t[-1] ^ 32; |
|
3802 t[-1] = Meta; |
|
3803 t++; |
|
3804 } |
|
3805 if (fromwhere == 6 && t != tmp) { |
|
3806 *misc = STOUC(tmp[0]); |
|
3807 return s + 1; |
|
3808 } |
|
3809 } |
|
3810 DPUTS(fromwhere == 4, "BUG: unterminated $' substitution"); |
|
3811 *t = '\0'; |
|
3812 if (fromwhere == 6) |
|
3813 *misc = 0; |
|
3814 else |
|
3815 *len = t - buf; |
|
3816 return buf; |
|
3817 } |
|
3818 |
|
3819 /* Return non-zero if s is a prefix of t. */ |
|
3820 |
|
3821 /**/ |
|
3822 mod_export int |
|
3823 strpfx(char *s, char *t) |
|
3824 { |
|
3825 while (*s && *s == *t) |
|
3826 s++, t++; |
|
3827 return !*s; |
|
3828 } |
|
3829 |
|
3830 /* Return non-zero if s is a suffix of t. */ |
|
3831 |
|
3832 /**/ |
|
3833 mod_export int |
|
3834 strsfx(char *s, char *t) |
|
3835 { |
|
3836 int ls = strlen(s), lt = strlen(t); |
|
3837 |
|
3838 if (ls <= lt) |
|
3839 return !strcmp(t + lt - ls, s); |
|
3840 return 0; |
|
3841 } |
|
3842 |
|
3843 /**/ |
|
3844 static int |
|
3845 upchdir(int n) |
|
3846 { |
|
3847 char buf[PATH_MAX]; |
|
3848 char *s; |
|
3849 int err = -1; |
|
3850 |
|
3851 while (n > 0) { |
|
3852 for (s = buf; s < buf + PATH_MAX - 4 && n--; ) |
|
3853 *s++ = '.', *s++ = '.', *s++ = '\\'; |
|
3854 s[-1] = '\0'; |
|
3855 if (chdir(buf)) |
|
3856 return err; |
|
3857 err = -2; |
|
3858 } |
|
3859 return 0; |
|
3860 } |
|
3861 |
|
3862 /* Change directory, without following symlinks. Returns 0 on success, -1 * |
|
3863 * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If * |
|
3864 * fchdir() fails, or the current directory is unreadable, we might end up * |
|
3865 * in an unwanted directory in case of failure. */ |
|
3866 |
|
3867 /**/ |
|
3868 mod_export int |
|
3869 lchdir(char const *path, struct dirsav *d, int hard) |
|
3870 { |
|
3871 char const *pptr; |
|
3872 int level; |
|
3873 struct stat st1; |
|
3874 struct dirsav ds; |
|
3875 #ifdef HAVE_LSTAT |
|
3876 char buf[PATH_MAX + 1], *ptr; |
|
3877 int err; |
|
3878 struct stat st2; |
|
3879 #endif |
|
3880 |
|
3881 if (!d) { |
|
3882 ds.ino = ds.dev = 0; |
|
3883 ds.dirname = NULL; |
|
3884 ds.dirfd = -1; |
|
3885 d = &ds; |
|
3886 } |
|
3887 #ifdef HAVE_LSTAT |
|
3888 if ((*path == '\\' || !hard) && |
|
3889 (d != &ds || hard)){ |
|
3890 #else |
|
3891 if (*path == '\\') { |
|
3892 #endif |
|
3893 level = 0; |
|
3894 #ifdef HAVE_FCHDIR |
|
3895 if (d->dirfd < 0 && (d->dirfd = open(".", O_RDONLY | O_NOCTTY)) < 0 && |
|
3896 zgetdir(d) && *d->dirname != '\\') |
|
3897 d->dirfd = open("..", O_RDONLY | O_NOCTTY); |
|
3898 #else |
|
3899 if (!d->dirname) |
|
3900 zgetdir(d); |
|
3901 #endif |
|
3902 } else { |
|
3903 level = 0; |
|
3904 if (!d->dev && !d->ino) { |
|
3905 stat(".", &st1); |
|
3906 d->dev = st1.st_dev; |
|
3907 d->ino = st1.st_ino; |
|
3908 } |
|
3909 } |
|
3910 |
|
3911 #ifdef HAVE_LSTAT |
|
3912 if (!hard) |
|
3913 #endif |
|
3914 { |
|
3915 if (d != &ds) { |
|
3916 for (pptr = path; *pptr; level++) { |
|
3917 while (*pptr && *pptr++ != '\\'); |
|
3918 while (*pptr == '\\') |
|
3919 pptr++; |
|
3920 } |
|
3921 d->level = level; |
|
3922 } |
|
3923 return zchdir((char *) path); |
|
3924 } |
|
3925 #ifdef HAVE_LSTAT |
|
3926 if (*path == '\\') |
|
3927 chdir("\\"); |
|
3928 for(;;) { |
|
3929 while(*path == '\\') |
|
3930 path++; |
|
3931 if(!*path) { |
|
3932 if (d == &ds) { |
|
3933 zsfree(ds.dirname); |
|
3934 if (ds.dirfd >=0) |
|
3935 close(ds.dirfd); |
|
3936 } else |
|
3937 d->level = level; |
|
3938 return 0; |
|
3939 } |
|
3940 for(pptr = path; *++pptr && *pptr != '\\'; ) ; |
|
3941 if(pptr - path > PATH_MAX) { |
|
3942 err = ENAMETOOLONG; |
|
3943 break; |
|
3944 } |
|
3945 for(ptr = buf; path != pptr; ) |
|
3946 *ptr++ = *path++; |
|
3947 *ptr = 0; |
|
3948 if(lstat(buf, &st1)) { |
|
3949 err = errno; |
|
3950 break; |
|
3951 } |
|
3952 if(!S_ISDIR(st1.st_mode)) { |
|
3953 err = ENOTDIR; |
|
3954 break; |
|
3955 } |
|
3956 if(chdir(buf)) { |
|
3957 err = errno; |
|
3958 break; |
|
3959 } |
|
3960 if (level >= 0) |
|
3961 level++; |
|
3962 if(lstat(".", &st2)) { |
|
3963 err = errno; |
|
3964 break; |
|
3965 } |
|
3966 if(st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) { |
|
3967 err = ENOTDIR; |
|
3968 break; |
|
3969 } |
|
3970 } |
|
3971 if (restoredir(d)) { |
|
3972 if (d == &ds) { |
|
3973 zsfree(ds.dirname); |
|
3974 if (ds.dirfd >=0) |
|
3975 close(ds.dirfd); |
|
3976 } |
|
3977 errno = err; |
|
3978 return -2; |
|
3979 } |
|
3980 if (d == &ds) { |
|
3981 zsfree(ds.dirname); |
|
3982 if (ds.dirfd >=0) |
|
3983 close(ds.dirfd); |
|
3984 } |
|
3985 errno = err; |
|
3986 return -1; |
|
3987 #endif /* HAVE_LSTAT */ |
|
3988 } |
|
3989 |
|
3990 /**/ |
|
3991 mod_export int |
|
3992 restoredir(struct dirsav *d) |
|
3993 { |
|
3994 int err = 0; |
|
3995 struct stat sbuf; |
|
3996 |
|
3997 if (d->dirname && *d->dirname == '\\') |
|
3998 return chdir(d->dirname); |
|
3999 #ifdef HAVE_FCHDIR |
|
4000 if (d->dirfd >= 0) { |
|
4001 if (!fchdir(d->dirfd)) { |
|
4002 if (!d->dirname) { |
|
4003 return 0; |
|
4004 } else if (chdir(d->dirname)) { |
|
4005 close(d->dirfd); |
|
4006 d->dirfd = -1; |
|
4007 err = -2; |
|
4008 } |
|
4009 } else { |
|
4010 close(d->dirfd); |
|
4011 d->dirfd = err = -1; |
|
4012 } |
|
4013 } else |
|
4014 #endif |
|
4015 if (d->level > 0) |
|
4016 err = upchdir(d->level); |
|
4017 else if (d->level < 0) |
|
4018 err = -1; |
|
4019 if (d->dev || d->ino) { |
|
4020 stat(".", &sbuf); |
|
4021 if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) |
|
4022 err = -2; |
|
4023 } |
|
4024 return err; |
|
4025 } |
|
4026 |
|
4027 |
|
4028 /* Check whether the shell is running with privileges in effect. * |
|
4029 * This is the case if EITHER the euid is zero, OR (if the system * |
|
4030 * supports POSIX.1e (POSIX.6) capability sets) the process' * |
|
4031 * Effective or Inheritable capability sets are non-empty. */ |
|
4032 |
|
4033 /**/ |
|
4034 int |
|
4035 privasserted(void) |
|
4036 { |
|
4037 if(!geteuid()) |
|
4038 return 1; |
|
4039 #ifdef HAVE_CAP_GET_PROC |
|
4040 { |
|
4041 cap_t caps = cap_get_proc(); |
|
4042 if(caps) { |
|
4043 /* POSIX doesn't define a way to test whether a capability set * |
|
4044 * is empty or not. Typical. I hope this is conforming... */ |
|
4045 cap_flag_value_t val; |
|
4046 cap_value_t n; |
|
4047 for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++) |
|
4048 if(val) { |
|
4049 cap_free(caps); |
|
4050 return 1; |
|
4051 } |
|
4052 cap_free(caps); |
|
4053 } |
|
4054 } |
|
4055 #endif /* HAVE_CAP_GET_PROC */ |
|
4056 return 0; |
|
4057 } |
|
4058 |
|
4059 #ifdef DEBUG |
|
4060 |
|
4061 /**/ |
|
4062 mod_export void |
|
4063 dputs(char *message) |
|
4064 { |
|
4065 fprintf(stderr, "%s\n", message); |
|
4066 fflush(stderr); |
|
4067 } |
|
4068 |
|
4069 #endif /* DEBUG */ |
|
4070 |
|
4071 /**/ |
|
4072 mod_export int |
|
4073 mode_to_octal(mode_t mode) |
|
4074 { |
|
4075 int m = 0; |
|
4076 |
|
4077 if(mode & S_ISUID) |
|
4078 m |= 04000; |
|
4079 if(mode & S_ISGID) |
|
4080 m |= 02000; |
|
4081 if(mode & S_ISVTX) |
|
4082 m |= 01000; |
|
4083 if(mode & S_IRUSR) |
|
4084 m |= 00400; |
|
4085 if(mode & S_IWUSR) |
|
4086 m |= 00200; |
|
4087 if(mode & S_IXUSR) |
|
4088 m |= 00100; |
|
4089 if(mode & S_IRGRP) |
|
4090 m |= 00040; |
|
4091 if(mode & S_IWGRP) |
|
4092 m |= 00020; |
|
4093 if(mode & S_IXGRP) |
|
4094 m |= 00010; |
|
4095 if(mode & S_IROTH) |
|
4096 m |= 00004; |
|
4097 if(mode & S_IWOTH) |
|
4098 m |= 00002; |
|
4099 if(mode & S_IXOTH) |
|
4100 m |= 00001; |
|
4101 return m; |
|
4102 } |
|
4103 |
|
4104 #ifdef MAILDIR_SUPPORT |
|
4105 /* |
|
4106 * Stat a file. If it's a maildir, check all messages |
|
4107 * in the maildir and present the grand total as a file. |
|
4108 * The fields in the 'struct stat' are from the mail directory. |
|
4109 * The following fields are emulated: |
|
4110 * |
|
4111 * st_nlink always 1 |
|
4112 * st_size total number of bytes in all files |
|
4113 * st_blocks total number of messages |
|
4114 * st_atime access time of newest file in maildir |
|
4115 * st_mtime modify time of newest file in maildir |
|
4116 * st_mode S_IFDIR changed to S_IFREG |
|
4117 * |
|
4118 * This is good enough for most mail-checking applications. |
|
4119 */ |
|
4120 |
|
4121 /**/ |
|
4122 int |
|
4123 mailstat(char *path, struct stat *st) |
|
4124 { |
|
4125 DIR *dd; |
|
4126 struct dirent *fn; |
|
4127 struct stat st_ret, st_tmp; |
|
4128 static struct stat st_ret_last; |
|
4129 char *dir, *file = 0; |
|
4130 int i; |
|
4131 time_t atime = 0, mtime = 0; |
|
4132 size_t plen = strlen(path), dlen; |
|
4133 |
|
4134 /* First see if it's a directory. */ |
|
4135 if ((i = stat(path, st)) != 0 || !S_ISDIR(st->st_mode)) |
|
4136 return i; |
|
4137 |
|
4138 st_ret = *st; |
|
4139 st_ret.st_nlink = 1; |
|
4140 st_ret.st_size = 0; |
|
4141 st_ret.st_blocks = 0; |
|
4142 st_ret.st_mode &= ~S_IFDIR; |
|
4143 st_ret.st_mode |= S_IFREG; |
|
4144 |
|
4145 /* See if cur/ is present */ |
|
4146 dir = appstr(ztrdup(path), "/cur"); |
|
4147 if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; |
|
4148 st_ret.st_atime = st_tmp.st_atime; |
|
4149 |
|
4150 /* See if tmp/ is present */ |
|
4151 dir[plen] = 0; |
|
4152 dir = appstr(dir, "/tmp"); |
|
4153 if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; |
|
4154 st_ret.st_mtime = st_tmp.st_mtime; |
|
4155 |
|
4156 /* And new/ */ |
|
4157 dir[plen] = 0; |
|
4158 dir = appstr(dir, "/new"); |
|
4159 if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; |
|
4160 st_ret.st_mtime = st_tmp.st_mtime; |
|
4161 |
|
4162 #if THERE_IS_EXACTLY_ONE_MAILDIR_IN_MAILPATH |
|
4163 { |
|
4164 static struct stat st_new_last; |
|
4165 /* Optimization - if new/ didn't change, nothing else did. */ |
|
4166 if (st_tmp.st_dev == st_new_last.st_dev && |
|
4167 st_tmp.st_ino == st_new_last.st_ino && |
|
4168 st_tmp.st_atime == st_new_last.st_atime && |
|
4169 st_tmp.st_mtime == st_new_last.st_mtime) { |
|
4170 *st = st_ret_last; |
|
4171 return 0; |
|
4172 } |
|
4173 st_new_last = st_tmp; |
|
4174 } |
|
4175 #endif |
|
4176 |
|
4177 /* Loop over new/ and cur/ */ |
|
4178 for (i = 0; i < 2; i++) { |
|
4179 dir[plen] = 0; |
|
4180 dir = appstr(dir, i ? "/cur" : "/new"); |
|
4181 if ((dd = opendir(dir)) == NULL) { |
|
4182 zsfree(file); |
|
4183 zsfree(dir); |
|
4184 return 0; |
|
4185 } |
|
4186 dlen = strlen(dir) + 1; /* include the "/" */ |
|
4187 while ((fn = readdir(dd)) != NULL) { |
|
4188 if (fn->d_name[0] == '.') |
|
4189 continue; |
|
4190 if (file) { |
|
4191 file[dlen] = 0; |
|
4192 file = appstr(file, fn->d_name); |
|
4193 } else { |
|
4194 file = tricat(dir, "/", fn->d_name); |
|
4195 } |
|
4196 if (stat(file, &st_tmp) != 0) |
|
4197 continue; |
|
4198 st_ret.st_size += st_tmp.st_size; |
|
4199 st_ret.st_blocks++; |
|
4200 if (st_tmp.st_atime != st_tmp.st_mtime && |
|
4201 st_tmp.st_atime > atime) |
|
4202 atime = st_tmp.st_atime; |
|
4203 if (st_tmp.st_mtime > mtime) |
|
4204 mtime = st_tmp.st_mtime; |
|
4205 } |
|
4206 closedir(dd); |
|
4207 } |
|
4208 zsfree(file); |
|
4209 zsfree(dir); |
|
4210 |
|
4211 if (atime) st_ret.st_atime = atime; |
|
4212 if (mtime) st_ret.st_mtime = mtime; |
|
4213 |
|
4214 *st = st_ret_last = st_ret; |
|
4215 return 0; |
|
4216 } |
|
4217 #endif |