source: trunk/base/src/pextlib1.0/Pextlib.c @ 13644

Last change on this file since 13644 was 13644, checked in by pguyot (Paul Guyot), 15 years ago

New checksum type: rmd160.

  • Property svn:eol-style set to native
File size: 27.1 KB
Line 
1/*
2 * Pextlib.c
3 * $Id: Pextlib.c,v 1.83 2005/08/24 01:59:44 pguyot Exp $
4 *
5 * Copyright (c) 2002 - 2003 Apple Computer, Inc.
6 * Copyright (c) 2004 - 2005 Paul Guyot <pguyot@kallisys.net>
7 * Copyright (c) 2004 Landon Fuller <landonf@opendarwin.org>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#if HAVE_CONFIG_H
36#include <config.h>
37#endif
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <ctype.h>
42#include <errno.h>
43#include <grp.h>
44
45#if HAVE_STRING_H
46#include <string.h>
47#endif
48
49#if HAVE_STRINGS_H
50#include <strings.h>
51#endif
52
53#if HAVE_DIRENT_H
54#include <dirent.h>
55#endif
56
57#if HAVE_LIMITS_H
58#include <limits.h>
59#endif
60
61#if HAVE_PATHS_H
62#include <paths.h>
63#endif
64
65#ifndef _PATH_DEVNULL
66#define _PATH_DEVNULL   "/dev/null"
67#endif
68
69#include <pwd.h>
70
71#if HAVE_SYS_FILE_H
72#include <sys/file.h>
73#endif
74
75#if HAVE_SYS_TYPES_H
76#include <sys/types.h>
77#endif
78
79#if HAVE_SYS_FCNTL_H
80#include <sys/fcntl.h>
81#endif
82
83#if HAVE_FCNTL_H
84#include <fcntl.h>
85#endif
86
87#if HAVE_SYS_WAIT_H
88#include <sys/wait.h>
89#endif
90
91#if HAVE_UNISTD_H
92#include <unistd.h>
93#endif
94
95#if HAVE_SYS_SOCKET_H
96#include <sys/socket.h>
97#endif
98
99#if HAVE_SYS_STAT_H
100#include <sys/stat.h>
101#endif
102
103#include <tcl.h>
104
105#include "md5cmd.h"
106#include "sha1cmd.h"
107#include "rmd160cmd.h"
108#include "find.h"
109#include "filemap.h"
110#include "curl.h"
111#include "xinstall.h"
112#include "vercomp.h"
113#include "compat.h"
114
115#if HAVE_CRT_EXTERNS_H
116#include <crt_externs.h>
117#define environ (*_NSGetEnviron())
118#else
119extern char **environ;
120#endif
121
122#if !HAVE_BZERO
123#if HAVE_MEMSET
124#define bzero(b, len) (void)memset(b, 0x00, len)
125#endif
126#endif
127
128#if !HAVE_FGETLN
129char *fgetln(FILE *stream, size_t *len);
130#endif
131
132#define CBUFSIZ 30
133
134char *ui_escape(const char *source)
135{
136        char *d, *dest;
137        const char *s;
138        int slen, dlen;
139
140        s = source;
141        slen = dlen = strlen(source) * 2 + 1;
142        d = dest = malloc(dlen);
143        if (dest == NULL) {
144                return NULL;
145        }
146        while(*s != '\0') {
147                switch(*s) {
148                        case '\\':
149                        case '}':
150                        case '{':
151                                *d = '\\';
152                                d++;
153                                *d = *s;
154                                d++;
155                                s++;
156                                break;
157                        case '\n':
158                                s++;
159                                break;
160                        default:
161                                *d = *s;
162                                d++;
163                                s++;
164                                break;
165                }
166        }
167        *d = '\0';
168        return dest;
169}
170
171int ui_info(Tcl_Interp *interp, char *mesg)
172{
173        const char ui_proc_start[] = "ui_info [subst -nocommands -novariables {";
174        const char ui_proc_end[] = "}]";
175        char *script, *string, *p;
176        int scriptlen, len, rval;
177
178        string = ui_escape(mesg);
179        if (string == NULL)
180                return TCL_ERROR;
181
182        len = strlen(string);
183        scriptlen = sizeof(ui_proc_start) + len + sizeof(ui_proc_end) - 1;
184        script = malloc(scriptlen);
185        if (script == NULL)
186                return TCL_ERROR;
187        else
188                p = script;
189
190        memcpy(script, ui_proc_start, sizeof(ui_proc_start));
191        strcat(script, string);
192        strcat(script, ui_proc_end);
193        free(string);
194        rval = Tcl_EvalEx(interp, script, scriptlen - 1, 0);
195        free(script);
196        return rval;
197}
198
199struct linebuf {
200        size_t len;
201        char *line;
202};
203
204int SystemCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
205{
206        char *buf;
207        struct linebuf circbuf[CBUFSIZ];
208        size_t linelen;
209        char *args[4];
210        char *cmdstring;
211        FILE *pdes;
212        int fdset[2], nullfd;
213        int fline, pos, ret;
214        int osetsid = 0;
215        pid_t pid;
216        Tcl_Obj *errbuf;
217        Tcl_Obj *tcl_result;
218
219        if (objc != 2 && objc != 3) {
220                Tcl_WrongNumArgs(interp, 1, objv, "command");
221                return TCL_ERROR;
222        }
223       
224        if (objc == 3) {
225                char *arg = Tcl_GetString(objv[1]);
226                cmdstring = Tcl_GetString(objv[2]);
227
228                if (!strcmp(arg, "-notty")) {
229                        osetsid = 1;
230                } else {
231                        tcl_result = Tcl_NewStringObj("bad option ", -1);
232                        Tcl_AppendObjToObj(tcl_result, Tcl_NewStringObj(arg, -1));
233                        Tcl_SetObjResult(interp, tcl_result);
234                        return TCL_ERROR;
235                }
236        } else {
237                cmdstring = Tcl_GetString(objv[1]);
238        }
239
240        if (pipe(fdset) == -1)
241                return TCL_ERROR;
242
243        /*
244         * Fork a child to run the command, in a popen() like fashion -
245         * popen() itself is not used because stderr is also desired.
246         */
247        pid = fork();
248        if (pid == -1)
249                return TCL_ERROR;
250        if (pid == 0) {
251                close(fdset[0]);
252                if ((nullfd = open(_PATH_DEVNULL, O_RDONLY)) == -1)
253                        _exit(1);
254                dup2(nullfd, STDIN_FILENO);
255                dup2(fdset[1], STDOUT_FILENO);
256                dup2(fdset[1], STDERR_FILENO);
257                /* drop the controlling terminal if requested */
258                if (osetsid) {
259                        if (setsid() == -1)
260                                _exit(1);
261                }
262                /* XXX ugly string constants */
263                args[0] = "sh";
264                args[1] = "-c";
265                args[2] = cmdstring;
266                args[3] = NULL;
267                execve("/bin/sh", args, environ);
268                _exit(1);
269        }
270        close(fdset[1]);
271        pdes = fdopen(fdset[0], "r");
272
273        /* read from simulated popen() pipe */
274        pos = 0;
275        bzero(circbuf, sizeof(circbuf));
276        while ((buf = fgetln(pdes, &linelen)) != NULL) {
277                char *sbuf;
278                int slen;
279
280                /*
281                 * Allocate enough space to insert a terminating
282                 * '\0' if the line is not terminated with a '\n'
283                 */
284                if (buf[linelen - 1] == '\n')
285                        slen = linelen;
286                else
287                        slen = linelen + 1;
288
289                if (circbuf[pos].len == 0)
290                        sbuf = malloc(slen);
291                else {
292                        sbuf = realloc(circbuf[pos].line, slen);
293                }
294
295                if (sbuf == NULL) {
296                        for (fline = pos; pos < fline + CBUFSIZ; pos++) {
297                                if (circbuf[pos % CBUFSIZ].len != 0)
298                                        free(circbuf[pos % CBUFSIZ].line);
299                        }
300                        return TCL_ERROR;
301                }
302
303                memcpy(sbuf, buf, linelen);
304                /* terminate line with '\0',replacing '\n' if it exists */
305                sbuf[slen - 1] = '\0';
306
307                circbuf[pos].line = sbuf;
308                circbuf[pos].len = slen;
309
310                if (pos++ == CBUFSIZ - 1)
311                        pos = 0;
312                ret = ui_info(interp, sbuf);
313                if (ret != TCL_OK) {
314                        for (fline = pos; pos < fline + CBUFSIZ; pos++) {
315                                if (circbuf[pos % CBUFSIZ].len != 0)
316                                        free(circbuf[pos % CBUFSIZ].line);
317                        }
318                        return ret;
319                }
320        }
321        fclose(pdes);
322
323        if (wait(&ret) != pid)
324                return TCL_ERROR;
325        if (WIFEXITED(ret)) {
326                if (WEXITSTATUS(ret) == 0)
327                        return TCL_OK;
328                else {
329                        /* Copy the contents of the circular buffer to errbuf */
330                        Tcl_Obj* errorCode;
331                        errbuf = Tcl_NewStringObj(NULL, 0);
332                        for (fline = pos; pos < fline + CBUFSIZ; pos++) {
333                                if (circbuf[pos % CBUFSIZ].len == 0)
334                                continue; /* skip empty lines */
335
336                                /* Append line, minus trailing NULL */
337                                Tcl_AppendToObj(errbuf, circbuf[pos % CBUFSIZ].line,
338                                                circbuf[pos % CBUFSIZ].len - 1);
339
340                                /* Re-add previously stripped newline */
341                                Tcl_AppendToObj(errbuf, "\n", 1);
342                                free(circbuf[pos % CBUFSIZ].line);
343                        }
344
345                        /* set errorCode [list CHILDSTATUS <pid> <code>] */
346                        errorCode = Tcl_NewListObj(0, NULL);
347                        Tcl_ListObjAppendElement(interp, errorCode, Tcl_NewStringObj("CHILDSTATUS", -1));
348                        Tcl_ListObjAppendElement(interp, errorCode, Tcl_NewIntObj(pid));
349                        Tcl_ListObjAppendElement(interp, errorCode, Tcl_NewIntObj(WEXITSTATUS(ret)));
350                        Tcl_SetObjErrorCode(interp, errorCode);
351
352                        /* set result */
353                        tcl_result = Tcl_NewStringObj("shell command \"", -1);
354                        Tcl_AppendToObj(tcl_result, cmdstring, -1);
355                        Tcl_AppendToObj(tcl_result, "\" returned error ", -1);
356                        Tcl_AppendObjToObj(tcl_result, Tcl_NewIntObj(WEXITSTATUS(ret)));
357                        Tcl_AppendToObj(tcl_result, "\nCommand output: ", -1);
358                        Tcl_AppendObjToObj(tcl_result, errbuf);
359                        Tcl_SetObjResult(interp, tcl_result);
360                        return TCL_ERROR;
361                }
362        } else
363                return TCL_ERROR;
364}
365
366int SudoCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
367{
368        char *buf;
369        struct linebuf circbuf[CBUFSIZ];
370        size_t linelen;
371        char *args[4];
372        char *cmdstring, *passwd;
373        FILE *pdes;
374        int fdset[2];
375        int fline, pos, ret;
376        pid_t pid;
377        Tcl_Obj *errbuf;
378        Tcl_Obj *tcl_result;
379
380        if (objc != 3) {
381                Tcl_WrongNumArgs(interp, 1, objv, "password command");
382                return TCL_ERROR;
383        }
384        passwd = Tcl_GetString(objv[1]);
385        cmdstring = Tcl_GetString(objv[2]);
386
387        if (pipe(fdset) == -1)
388                return TCL_ERROR;
389
390        /*
391         * Fork a child to run the command, in a popen() like fashion -
392         * popen() itself is not used because stderr is also desired.
393         */
394        pid = fork();
395        if (pid == -1)
396                return TCL_ERROR;
397        if (pid == 0) {
398                dup2(fdset[0], STDIN_FILENO);
399                dup2(fdset[1], STDOUT_FILENO);
400                dup2(fdset[1], STDERR_FILENO);
401                args[0] = "sudo";
402                args[1] = "-S";
403                args[2] = cmdstring;
404                args[3] = NULL;
405                execve("/usr/bin/sudo", args, environ);
406                /* Now throw away the privs we just acquired */
407                args[1] = "-k";
408                args[2] = NULL;
409                execve("/usr/bin/sudo", args, environ);
410                _exit(1);
411        } else {
412                write(fdset[1], passwd, strlen(passwd));
413                write(fdset[1], "\n", 1);
414                close(fdset[1]);
415        }
416        pdes = fdopen(fdset[0], "r");
417
418        /* read from simulated popen() pipe */
419        pos = 0;
420        bzero(circbuf, sizeof(circbuf));
421        while ((buf = fgetln(pdes, &linelen)) != NULL) {
422                char *sbuf;
423                int slen;
424
425                /*
426                 * Allocate enough space to insert a terminating
427                 * '\0' if the line is not terminated with a '\n'
428                 */
429                if (buf[linelen - 1] == '\n')
430                        slen = linelen;
431                else
432                        slen = linelen + 1;
433
434                if (circbuf[pos].len == 0)
435                        sbuf = malloc(slen);
436                else {
437                        sbuf = realloc(circbuf[pos].line, slen);
438                }
439
440                if (sbuf == NULL) {
441                        for (fline = pos; pos < fline + CBUFSIZ; pos++) {
442                                if (circbuf[pos % CBUFSIZ].len != 0)
443                                        free(circbuf[pos % CBUFSIZ].line);
444                        }
445                        return TCL_ERROR;
446                }
447
448                memcpy(sbuf, buf, linelen);
449                /* terminate line with '\0',replacing '\n' if it exists */
450                sbuf[slen - 1] = '\0';
451
452                circbuf[pos].line = sbuf;
453                circbuf[pos].len = slen;
454
455                if (pos++ == CBUFSIZ - 1)
456                        pos = 0;
457                ret = ui_info(interp, sbuf);
458                if (ret != TCL_OK) {
459                        for (fline = pos; pos < fline + CBUFSIZ; pos++) {
460                                if (circbuf[pos % CBUFSIZ].len != 0)
461                                        free(circbuf[pos % CBUFSIZ].line);
462                        }
463                        return ret;
464                }
465        }
466        fclose(pdes);
467
468        if (wait(&ret) != pid)
469                return TCL_ERROR;
470        if (WIFEXITED(ret)) {
471                if (WEXITSTATUS(ret) == 0)
472                        return TCL_OK;
473                else {
474                        /* Copy the contents of the circular buffer to errbuf */
475                        Tcl_Obj* errorCode;
476                        errbuf = Tcl_NewStringObj(NULL, 0);
477                        for (fline = pos; pos < fline + CBUFSIZ; pos++) {
478                                if (circbuf[pos % CBUFSIZ].len == 0)
479                                continue; /* skip empty lines */
480
481                                /* Append line, minus trailing NULL */
482                                Tcl_AppendToObj(errbuf, circbuf[pos % CBUFSIZ].line,
483                                                circbuf[pos % CBUFSIZ].len - 1);
484
485                                /* Re-add previously stripped newline */
486                                Tcl_AppendToObj(errbuf, "\n", 1);
487                                free(circbuf[pos % CBUFSIZ].line);
488                        }
489
490                        /* set errorCode [list CHILDSTATUS <pid> <code>] */
491                        errorCode = Tcl_NewListObj(0, NULL);
492                        Tcl_ListObjAppendElement(interp, errorCode, Tcl_NewStringObj("CHILDSTATUS", -1));
493                        Tcl_ListObjAppendElement(interp, errorCode, Tcl_NewIntObj(pid));
494                        Tcl_ListObjAppendElement(interp, errorCode, Tcl_NewIntObj(WEXITSTATUS(ret)));
495                        Tcl_SetObjErrorCode(interp, errorCode);
496
497                        /* set result */
498                        tcl_result = Tcl_NewStringObj("sudo command \"", -1);
499                        Tcl_AppendToObj(tcl_result, cmdstring, -1);
500                        Tcl_AppendToObj(tcl_result, "\" returned error ", -1);
501                        Tcl_AppendObjToObj(tcl_result, Tcl_NewIntObj(WEXITSTATUS(ret)));
502                        Tcl_AppendToObj(tcl_result, "\nCommand output: ", -1);
503                        Tcl_AppendObjToObj(tcl_result, errbuf);
504                        Tcl_SetObjResult(interp, tcl_result);
505                        return TCL_ERROR;
506                }
507        } else
508                return TCL_ERROR;
509}
510
511int FlockCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
512{
513        static const char errorstr[] = "use one of \"-shared\", \"-exclusive\", or \"-unlock\", and optionally \"-noblock\"";
514        int operation = 0, fd, i, ret;
515        int errnoval = 0;
516        int oshared = 0, oexclusive = 0, ounlock = 0, onoblock = 0;
517#if defined(HAVE_LOCKF) && !defined(HAVE_FLOCK)
518        off_t curpos;
519#endif
520        char *res;
521        Tcl_Channel channel;
522        ClientData handle;
523
524        if (objc < 3 || objc > 4) {
525                Tcl_WrongNumArgs(interp, 1, objv, "channelId switches");
526                return TCL_ERROR;
527        }
528
529        if ((channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL)) == NULL)
530                return TCL_ERROR;
531
532        if (Tcl_GetChannelHandle(channel, TCL_READABLE|TCL_WRITABLE, &handle) != TCL_OK) {
533                Tcl_SetResult(interp, "error getting channel handle", TCL_STATIC);
534                return TCL_ERROR;
535        }
536        fd = (int) handle;
537
538        for (i = 2; i < objc; i++) {
539                char *arg = Tcl_GetString(objv[i]);
540                if (!strcmp(arg, "-shared")) {
541                  oshared = 1;
542                } else if (!strcmp(arg, "-exclusive")) {
543                  oexclusive = 1;
544                } else if (!strcmp(arg, "-unlock")) {
545                  ounlock = 1;
546                } else if (!strcmp(arg, "-noblock")) {
547                  onoblock = 1;
548                }
549        }
550
551        /* verify the arguments */
552
553        if((oshared + oexclusive + ounlock) != 1) {
554          /* only one of the options should have been specified */
555          Tcl_SetResult(interp, (void *) &errorstr, TCL_STATIC);
556          return TCL_ERROR;
557        }
558
559        if(onoblock && ounlock) {
560          /* should not be specified together */
561          Tcl_SetResult(interp, "-noblock can not be used with -unlock", TCL_STATIC);
562          return TCL_ERROR;
563        }
564         
565#if HAVE_FLOCK
566        /* prefer flock if present */
567        if(oshared) operation |= LOCK_SH;
568
569        if(oexclusive) operation |= LOCK_EX;
570
571        if(ounlock) operation |= LOCK_UN;
572
573        if(onoblock) operation |= LOCK_NB;
574
575        ret = flock(fd, operation);
576        if(ret == -1) {
577          errnoval = errno;
578        }
579#else
580#if HAVE_LOCKF
581        if(ounlock) operation = F_ULOCK;
582
583        /* lockf semantics don't map to shared locks. */
584        if(oshared || oexclusive) {
585          if(onoblock) {
586            operation = F_TLOCK;
587          } else {
588            operation = F_LOCK;
589          }
590        }
591
592        curpos = lseek(fd, 0, SEEK_CUR);
593        if(curpos == -1) {
594                Tcl_SetResult(interp, (void *) "Seek error", TCL_STATIC);
595                return TCL_ERROR;
596        }
597
598        ret = lockf(fd, operation, 0); /* lock entire file */
599
600        curpos = lseek(fd, curpos, SEEK_SET);
601        if(curpos == -1) {
602                Tcl_SetResult(interp, (void *) "Seek error", TCL_STATIC);
603                return TCL_ERROR;
604        }
605
606        if(ret == -1) {
607          errnoval = errno;
608          if((oshared || oexclusive)) {
609            /* map the errno val to what we would expect for flock */
610            if(onoblock && errnoval == EAGAIN) {
611              /* on some systems, EAGAIN=EWOULDBLOCK, but lets be safe */
612              errnoval = EWOULDBLOCK;
613            } else if(errnoval == EINVAL) {
614              errnoval = EOPNOTSUPP;
615            }
616          }
617        }
618#else
619#error no available locking implementation
620#endif /* HAVE_LOCKF */
621#endif /* HAVE_FLOCK */
622
623        if (ret != 0)
624        {
625                switch(errnoval) {
626                        case EAGAIN:
627                                res = "EAGAIN";
628                                break;
629                        case EBADF:
630                                res = "EBADF";
631                                break;
632                        case EINVAL:
633                                res = "EINVAL";
634                                break;
635                        case EOPNOTSUPP:
636                                res = "EOPNOTSUPP";
637                                break;
638                        default:
639                                res = strerror(errno);
640                                break;
641                }
642                Tcl_SetResult(interp, (void *) res, TCL_STATIC);
643                return TCL_ERROR;
644        }
645        return TCL_OK;
646}
647
648/**
649 *
650 * Return the list of elements in a directory.
651 * Since 1.60.4.2, the list doesn't include . and ..
652 *
653 * Synopsis: readdir directory
654 */
655int ReaddirCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
656{
657        DIR *dirp;
658        struct dirent *dp;
659        Tcl_Obj *tcl_result;
660        char *path;
661
662        if (objc != 2) {
663                Tcl_WrongNumArgs(interp, 1, objv, "directory");
664                return TCL_ERROR;
665        }
666
667        path = Tcl_GetString(objv[1]);
668        dirp = opendir(path);
669        if (!dirp) {
670                Tcl_SetResult(interp, "Cannot read directory", TCL_STATIC);
671                return TCL_ERROR;
672        }
673        tcl_result = Tcl_NewListObj(0, NULL);
674        while ((dp = readdir(dirp))) {
675                /* Skip . and .. */
676                if ((dp->d_name[0] != '.') ||
677                        ((dp->d_name[1] != 0)   /* "." */
678                                &&
679                        ((dp->d_name[1] != '.') || (dp->d_name[2] != 0)))) /* ".." */ {
680                        Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(dp->d_name, -1));
681                }
682        }
683        closedir(dirp);
684        Tcl_SetObjResult(interp, tcl_result);
685       
686        return TCL_OK;
687}
688
689int StrsedCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
690{
691        char *pattern, *string, *res;
692        int range[2];
693        extern char *strsed(char *str, char *pat, int *range);
694        Tcl_Obj *tcl_result;
695
696        if (objc != 3) {
697                Tcl_WrongNumArgs(interp, 1, objv, "string pattern");
698                return TCL_ERROR;
699        }
700
701        string = Tcl_GetString(objv[1]);
702        pattern = Tcl_GetString(objv[2]);
703        res = strsed(string, pattern, range);
704        if (!res) {
705                Tcl_SetResult(interp, "strsed failed", TCL_STATIC);
706                return TCL_ERROR;
707        }
708        tcl_result = Tcl_NewStringObj(res, -1);
709        Tcl_SetObjResult(interp, tcl_result);
710        free(res);
711        return TCL_OK;
712}
713
714/**
715 * Take a file descriptor and generate a Tcl channel out of it.
716 * Syntax is:
717 * mkchannelfromfd fd [r|w|rw]
718 * Use r to generate a read-only channel, w for a write only channel or rw
719 * for a read/write channel (the default).
720 */
721int MkChannelFromFdCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
722{
723        Tcl_Channel theChannel;
724        int fd;
725        int readOrWrite = TCL_READABLE | TCL_WRITABLE;
726
727        if ((objc != 2) && (objc != 3)) {
728                Tcl_WrongNumArgs(interp, 1, objv, "fd [r|w|rw]");
729                return TCL_ERROR;
730        }
731       
732        if (objc == 3) {
733                char* readOrWrite_as_char_star;
734                readOrWrite_as_char_star = strdup(Tcl_GetString(objv[2]));
735                if (readOrWrite_as_char_star == NULL) {
736                        return TCL_ERROR;
737                }
738
739                if ((readOrWrite_as_char_star[0] == 'r')
740                        && (readOrWrite_as_char_star[1] == '\0')) {
741                        readOrWrite = TCL_READABLE;
742                } else if ((readOrWrite_as_char_star[0] == 'w')
743                        && (readOrWrite_as_char_star[1] == '\0')) {
744                        readOrWrite = TCL_WRITABLE;
745                } else if ((readOrWrite_as_char_star[0] == 'r')
746                        && (readOrWrite_as_char_star[1] == 'w')
747                        && (readOrWrite_as_char_star[2] == '\0')) {
748                        readOrWrite = TCL_READABLE | TCL_WRITABLE;
749                } else {
750                        Tcl_AppendResult(interp, "Bad mode. Use r, w or rw", NULL);
751                        free(readOrWrite_as_char_star);
752                        return TCL_ERROR;
753                }
754
755                free(readOrWrite_as_char_star);
756        }
757
758        {
759                char* fd_as_char_star;
760                fd_as_char_star = strdup(Tcl_GetString(objv[1]));
761                if (fd_as_char_star == NULL) {
762                        return TCL_ERROR;
763                }
764
765                if (Tcl_GetInt(interp, fd_as_char_star, &fd) != TCL_OK) {
766                        free(fd_as_char_star);
767                        return TCL_ERROR;
768                }
769                free(fd_as_char_star);
770        }
771
772        theChannel = Tcl_MakeFileChannel((ClientData) fd, readOrWrite);
773        if (theChannel == NULL) {
774                return TCL_ERROR;
775        }
776       
777        /* register the channel in the current interpreter */
778        Tcl_RegisterChannel(interp, theChannel);
779        Tcl_AppendResult(interp, Tcl_GetChannelName(theChannel), (char *) NULL);
780
781        return TCL_OK;
782}
783
784int MktempCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
785{
786        char *template, *sp;
787        Tcl_Obj *tcl_result;
788
789        if (objc != 2) {
790                Tcl_WrongNumArgs(interp, 1, objv, "template");
791                return TCL_ERROR;
792        }
793
794        template = strdup(Tcl_GetString(objv[1]));
795        if (template == NULL)
796                return TCL_ERROR;
797
798        if ((sp = mktemp(template)) == NULL) {
799                Tcl_AppendResult(interp, "mktemp failed: ", strerror(errno), NULL);
800                free(template);
801                return TCL_ERROR;
802        }
803
804        tcl_result = Tcl_NewStringObj(sp, -1);
805        Tcl_SetObjResult(interp, tcl_result);
806        free(sp);
807        free(template);
808        return TCL_OK;
809}
810
811int MkstempCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
812{
813        Tcl_Channel channel;
814        char *template, *channelname;
815        int fd;
816
817        if (objc != 2) {
818                Tcl_WrongNumArgs(interp, 1, objv, "template");
819                return TCL_ERROR;
820        }
821
822        template = strdup(Tcl_GetString(objv[1]));
823        if (template == NULL)
824                return TCL_ERROR;
825
826        if ((fd = mkstemp(template)) < 0) {
827                Tcl_AppendResult(interp, "mkstemp failed: ", strerror(errno), NULL);
828                free(template);
829                return TCL_ERROR;
830        }
831
832        channel = Tcl_MakeFileChannel((ClientData) fd, TCL_READABLE|TCL_WRITABLE);
833        Tcl_RegisterChannel(interp, channel);
834        channelname = (char *)Tcl_GetChannelName(channel);
835        Tcl_AppendResult(interp, channelname, " ", template, NULL);
836        free(template);
837        return TCL_OK;
838}
839
840/**
841 * Call mkfifo(2).
842 * Generate a Tcl error if something wrong occurred.
843 *
844 * Syntax is:
845 * mkfifo path mode
846 */
847int MkfifoCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
848{
849        char* path;
850        mode_t mode;
851
852        if (objc != 3) {
853                Tcl_WrongNumArgs(interp, 1, objv, "path mode");
854                return TCL_ERROR;
855        }
856       
857        {
858                char* mode_as_char_star;
859                int mode_as_int;
860                mode_as_char_star = strdup(Tcl_GetString(objv[2]));
861                if (mode_as_char_star == NULL) {
862                        return TCL_ERROR;
863                }
864
865                if (Tcl_GetInt(interp, mode_as_char_star, &mode_as_int) != TCL_OK) {
866                        free(mode_as_char_star);
867                        return TCL_ERROR;
868                }
869                free(mode_as_char_star);
870                mode = (mode_t) mode_as_int;
871        }
872
873        path = strdup(Tcl_GetString(objv[1]));
874        if (path == NULL) {
875                return TCL_ERROR;
876        }
877
878        if (mkfifo(path, mode) != 0) {
879                Tcl_AppendResult(interp, "mkfifo failed: ", strerror(errno), NULL);
880                free(path);
881                return TCL_ERROR;
882        }
883
884        free(path);
885        return TCL_OK;
886}
887
888int ExistsuserCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
889{
890        Tcl_Obj *tcl_result;
891        struct passwd *pwent;
892        char *user;
893
894        if (objc != 2) {
895                Tcl_WrongNumArgs(interp, 1, objv, "user");
896                return TCL_ERROR;
897        }
898
899        user = strdup(Tcl_GetString(objv[1]));
900        if (isdigit(*(user)))
901                pwent = getpwuid(strtol(user, 0, 0));
902        else
903                pwent = getpwnam(user);
904        free(user);
905
906        if (pwent == NULL)
907                tcl_result = Tcl_NewIntObj(0);
908        else
909                tcl_result = Tcl_NewIntObj(pwent->pw_uid);
910
911        Tcl_SetObjResult(interp, tcl_result);
912        return TCL_OK;
913}
914
915int ExistsgroupCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
916{
917        Tcl_Obj *tcl_result;
918        struct group *grent;
919        char *group;
920
921        if (objc != 2) {
922                Tcl_WrongNumArgs(interp, 1, objv, "groupname");
923                return TCL_ERROR;
924        }
925
926        group = strdup(Tcl_GetString(objv[1]));
927        if (isdigit(*(group)))
928                grent = getgrgid(strtol(group, 0, 0));
929        else
930                grent = getgrnam(group);
931        free(group);
932
933        if (grent == NULL)
934                tcl_result = Tcl_NewIntObj(0);
935        else
936                tcl_result = Tcl_NewIntObj(grent->gr_gid);
937
938        Tcl_SetObjResult(interp, tcl_result);
939        return TCL_OK;
940}
941
942int NextuidCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc UNUSED, Tcl_Obj *CONST objv[] UNUSED)
943{
944        Tcl_Obj *tcl_result;
945        struct passwd *pwent;
946        int max;
947
948        max = 0;
949
950        while ((pwent = getpwent()) != NULL)
951                if ((int)pwent->pw_uid > max)
952                        max = (int)pwent->pw_uid;
953       
954        tcl_result = Tcl_NewIntObj(max + 1);
955        Tcl_SetObjResult(interp, tcl_result);
956        return TCL_OK;
957}
958
959int NextgidCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc UNUSED, Tcl_Obj *CONST objv[] UNUSED)
960{
961        Tcl_Obj *tcl_result;
962        struct group *grent;
963        int max;
964
965        max = 0;
966
967        while ((grent = getgrent()) != NULL)
968                if ((int)grent->gr_gid > max)
969                        max = (int)grent->gr_gid;
970       
971        tcl_result = Tcl_NewIntObj(max + 1);
972        Tcl_SetObjResult(interp, tcl_result);
973        return TCL_OK;
974}
975
976int UmaskCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc UNUSED, Tcl_Obj *CONST objv[] UNUSED)
977{
978        Tcl_Obj *tcl_result;
979        char *tcl_mask, *p;
980        const size_t stringlen = 4; /* 3 digits & \0 */
981        int i;
982        mode_t *set;
983        mode_t newmode;
984        mode_t oldmode;
985
986        if (objc != 2) {
987                Tcl_WrongNumArgs(interp, 1, objv, "numask");
988                return TCL_ERROR;
989        }
990
991        tcl_mask = Tcl_GetString(objv[1]);
992        if ((set = setmode(tcl_mask)) == NULL) {
993                Tcl_SetResult(interp, "Invalid umask mode", TCL_STATIC);
994                return TCL_ERROR;
995        }
996
997        newmode = getmode(set, 0);
998
999        oldmode = umask(newmode);
1000
1001        tcl_mask = malloc(stringlen); /* 3 digits & \0 */
1002        if (!tcl_mask) {
1003                return TCL_ERROR;
1004        }
1005
1006        /* Totally gross and cool */
1007        p = tcl_mask + stringlen;
1008        *p = '\0';
1009        for (i = stringlen - 1; i > 0; i--) {
1010                p--;
1011                *p = (oldmode & 7) + '0';
1012                oldmode >>= 3;
1013        }
1014        if (*p != '0') {
1015                p--;
1016                *p = '0';
1017        }
1018
1019        tcl_result = Tcl_NewStringObj(p, -1);
1020        free(tcl_mask);
1021
1022        Tcl_SetObjResult(interp, tcl_result);
1023        return TCL_OK;
1024}
1025
1026/**
1027 * Call pipe(2) to create a pipe.
1028 * Syntax is:
1029 * pipe
1030 *
1031 * Generate a Tcl error if something goes wrong.
1032 * Return a list with the file descriptors of the pipe. The first item is the
1033 * readable fd.
1034 */
1035int PipeCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
1036{
1037        Tcl_Obj* result;
1038        int fildes[2];
1039
1040        if (objc != 1) {
1041                Tcl_WrongNumArgs(interp, 1, objv, NULL);
1042                return TCL_ERROR;
1043        }
1044       
1045        if (pipe(fildes) < 0) {
1046                Tcl_AppendResult(interp, "pipe failed: ", strerror(errno), NULL);
1047                return TCL_ERROR;
1048        }
1049       
1050        /* build a list out of the couple */
1051        result = Tcl_NewListObj(0, NULL);
1052        Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(fildes[0]));
1053        Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(fildes[1]));
1054        Tcl_SetObjResult(interp, result);
1055
1056        return TCL_OK;
1057}
1058
1059/**
1060 * Call socketpair to generate a socket pair in the Unix domain.
1061 * Syntax is:
1062 * unixsocketpair
1063 *
1064 * Generate a Tcl error if something goes wrong.
1065 * Return a list with the file descriptors of the pair.
1066 */
1067int UnixSocketPairCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
1068{
1069        Tcl_Obj* result;
1070        int pair[2];
1071
1072        if (objc != 1) {
1073                Tcl_WrongNumArgs(interp, 1, objv, NULL);
1074                return TCL_ERROR;
1075        }
1076       
1077        if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
1078                Tcl_AppendResult(interp, "socketpair failed: ", strerror(errno), NULL);
1079                return TCL_ERROR;
1080        }
1081       
1082        /* build a list out of the pair */
1083        result = Tcl_NewListObj(0, NULL);
1084        Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(pair[0]));
1085        Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(pair[1]));
1086        Tcl_SetObjResult(interp, result);
1087
1088        return TCL_OK;
1089}
1090
1091int Pextlib_Init(Tcl_Interp *interp)
1092{
1093        if (Tcl_InitStubs(interp, "8.3", 0) == NULL)
1094                return TCL_ERROR;
1095
1096        Tcl_CreateObjCommand(interp, "system", SystemCmd, NULL, NULL);
1097        Tcl_CreateObjCommand(interp, "flock", FlockCmd, NULL, NULL);
1098        Tcl_CreateObjCommand(interp, "readdir", ReaddirCmd, NULL, NULL);
1099        Tcl_CreateObjCommand(interp, "strsed", StrsedCmd, NULL, NULL);
1100        Tcl_CreateObjCommand(interp, "mkstemp", MkstempCmd, NULL, NULL);
1101        Tcl_CreateObjCommand(interp, "mktemp", MktempCmd, NULL, NULL);
1102        Tcl_CreateObjCommand(interp, "existsuser", ExistsuserCmd, NULL, NULL);
1103        Tcl_CreateObjCommand(interp, "existsgroup", ExistsgroupCmd, NULL, NULL);
1104        Tcl_CreateObjCommand(interp, "nextuid", NextuidCmd, NULL, NULL);
1105        Tcl_CreateObjCommand(interp, "nextgid", NextgidCmd, NULL, NULL);
1106        Tcl_CreateObjCommand(interp, "md5", MD5Cmd, NULL, NULL);
1107        Tcl_CreateObjCommand(interp, "xinstall", InstallCmd, NULL, NULL);
1108        Tcl_CreateObjCommand(interp, "find", FindCmd, NULL, NULL);
1109        Tcl_CreateObjCommand(interp, "filemap", FilemapCmd, NULL, NULL);
1110        Tcl_CreateObjCommand(interp, "rpm-vercomp", RPMVercompCmd, NULL, NULL);
1111        Tcl_CreateObjCommand(interp, "rmd160", RMD160Cmd, NULL, NULL);
1112        Tcl_CreateObjCommand(interp, "sha1", SHA1Cmd, NULL, NULL);
1113        Tcl_CreateObjCommand(interp, "compat", CompatCmd, NULL, NULL);
1114        Tcl_CreateObjCommand(interp, "umask", UmaskCmd, NULL, NULL);
1115        Tcl_CreateObjCommand(interp, "sudo", SudoCmd, NULL, NULL);
1116        Tcl_CreateObjCommand(interp, "mkfifo", MkfifoCmd, NULL, NULL);
1117        Tcl_CreateObjCommand(interp, "unixsocketpair", UnixSocketPairCmd, NULL, NULL);
1118        Tcl_CreateObjCommand(interp, "mkchannelfromfd", MkChannelFromFdCmd, NULL, NULL);
1119        Tcl_CreateObjCommand(interp, "pipe", PipeCmd, NULL, NULL);
1120        Tcl_CreateObjCommand(interp, "curl", CurlCmd, NULL, NULL);
1121
1122        if (Tcl_PkgProvide(interp, "Pextlib", "1.0") != TCL_OK)
1123                return TCL_ERROR;
1124
1125        /* init libcurl */
1126        CurlInit(interp);
1127
1128        return TCL_OK;
1129}
Note: See TracBrowser for help on using the repository browser.