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

Last change on this file since 816 was 816, checked in by jkh, 18 years ago

Add a rather important missing free()

  • Property svn:eol-style set to native
File size: 8.8 KB
Line 
1/*
2 * Pextlib.c
3 *
4 * Copyright (c) 2002 Apple Computer, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <errno.h>
35#include <string.h>
36#include <dirent.h>
37#include <paths.h>
38#include <sys/file.h>
39#include <sys/types.h>
40#include <sys/wait.h>
41#include <tcl.h>
42#include <unistd.h>
43#ifdef __APPLE__
44#include <crt_externs.h>
45#endif
46
47#define BUFSIZ 1024
48
49#if !defined(__APPLE__)
50extern char **environ;
51#endif
52
53char *ui_escape(const char *source)
54{
55        char *d, *dest;
56        const char *s;
57        int slen, dlen;
58
59        s = source;
60        slen = dlen = strlen(source) * 2 + 1;
61        d = dest = malloc(dlen);
62        if (dest == NULL) {
63                return NULL;
64        }
65        while(*s != '\0') {
66                switch(*s) {
67                        case '\\':
68                        case '}':
69                        case '{':
70                                *d = '\\';
71                                d++;
72                                *d = *s;
73                                d++;
74                                s++;
75                                break;
76                        case '\n':
77                                s++;
78                                break;
79                        default:
80                                *d = *s;
81                                d++;
82                                s++;
83                                break;
84                }
85        }
86        *d = '\0';
87        return dest;
88}
89
90static int ui_info(Tcl_Interp *interp, char *mesg)
91{
92        const char ui_proc_start[] = "ui_info [subst -nocommands -novariables {";
93        const char ui_proc_end[] = "}]";
94        char *script, *string, *p;
95        int scriptlen, len, rval;
96
97        string = ui_escape(mesg);
98        if (string == NULL)
99                return TCL_ERROR;
100
101        len = strlen(string);
102        scriptlen = sizeof(ui_proc_start) + len + sizeof(ui_proc_end) - 1;
103        script = malloc(scriptlen);
104        if (script == NULL)
105                return TCL_ERROR;
106        else
107                p = script;
108
109        memcpy(script, ui_proc_start, sizeof(ui_proc_start));
110        strcat(script, string);
111        strcat(script, ui_proc_end);
112        free(string);
113        rval = Tcl_EvalEx(interp, script, scriptlen - 1, 0);
114        free(script);
115        return rval;
116}
117
118int SystemCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
119{
120        char buf[BUFSIZ];
121        char *args[4];
122        char *cmdstring;
123        FILE *pdes;
124        int fdset[2], nullfd;
125        int ret;
126        pid_t pid;
127#if defined(__APPLE__)
128        char **environ;
129        environ = *_NSGetEnviron();
130#endif
131
132        if (objc != 2) {
133                Tcl_WrongNumArgs(interp, 1, objv, "command");
134                return TCL_ERROR;
135        }
136        cmdstring = Tcl_GetString(objv[1]);
137
138        if (pipe(fdset) == -1)
139                return TCL_ERROR;
140
141        /*
142         * Fork a child to run the command, in a popen() like fashion -
143         * popen() itself is not used because stderr is also desired.
144         */
145        pid = fork();
146        if (pid == -1)
147                return TCL_ERROR;
148        if (pid == 0) {
149                close(fdset[0]);
150                if ((nullfd = open(_PATH_DEVNULL, O_RDONLY)) == -1)
151                        _exit(1);
152                dup2(nullfd, STDIN_FILENO);
153                dup2(fdset[1], STDOUT_FILENO);
154                dup2(fdset[1], STDERR_FILENO);
155                /* XXX dropping the controlling terminal */
156                if (setsid() == -1)
157                        _exit(1);
158                /* XXX ugly string constants */
159                args[0] = "sh";
160                args[1] = "-c";
161                args[2] = cmdstring;
162                args[3] = NULL;
163                execve("/bin/sh", args, environ);
164                _exit(1);
165        }
166        close(fdset[1]);
167        pdes = fdopen(fdset[0], "r");
168
169        /* read from simulated popen() pipe */
170        while (fgets(buf, BUFSIZ, pdes) != NULL) {
171                int ret = ui_info(interp, buf);
172                if (ret != TCL_OK)
173                        return ret;
174        }
175        fclose(pdes);
176
177        if (wait(&ret) != pid)
178                return TCL_ERROR;
179        if (WIFEXITED(ret)) {
180                if (WEXITSTATUS(ret) == 0)
181                        return TCL_OK;
182                else
183                        return TCL_ERROR;
184        } else
185                return TCL_ERROR;
186}
187
188int FlockCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
189{
190        static const char errorstr[] = "use one of \"-shared\", \"-exclusive\", or \"-unlock\"";
191        int operation = 0, fd, i, ret;
192        char *res;
193        Tcl_Channel channel;
194        ClientData handle;
195
196        if (objc < 3 || objc > 4) {
197                Tcl_WrongNumArgs(interp, 1, objv, "channelId switches");
198                return TCL_ERROR;
199        }
200
201        if ((channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL)) == NULL)
202                return TCL_ERROR;
203
204        if (Tcl_GetChannelHandle(channel, TCL_READABLE|TCL_WRITABLE, &handle) != TCL_OK) {
205                Tcl_SetResult(interp, "error getting channel handle", TCL_STATIC);
206                return TCL_ERROR;
207        }
208        fd = (int) handle;
209
210        for (i = 2; i < objc; i++) {
211                char *arg = Tcl_GetString(objv[i]);
212                if (!strcmp(arg, "-shared")) {
213                        if (operation & LOCK_EX || operation & LOCK_UN) {
214                                Tcl_SetResult(interp, (void *) &errorstr, TCL_STATIC);
215                                return TCL_ERROR;
216                        }
217                        operation |= LOCK_SH;
218                } else if (!strcmp(arg, "-exclusive")) {
219                        if (operation & LOCK_SH || operation & LOCK_UN) {
220                                Tcl_SetResult(interp, (void *) &errorstr, TCL_STATIC);
221                                return TCL_ERROR;
222                        }
223                        operation |= LOCK_EX;
224                } else if (!strcmp(arg, "-unlock")) {
225                        if (operation & LOCK_SH || operation & LOCK_EX) {
226                                Tcl_SetResult(interp, (void *) &errorstr, TCL_STATIC);
227                                return TCL_ERROR;
228                        }
229                        operation |= LOCK_UN;
230                } else if (!strcmp(arg, "-noblock")) {
231                        if (operation & LOCK_UN) {
232                                Tcl_SetResult(interp, "-noblock can not be used with -unlock", TCL_STATIC);
233                                return TCL_ERROR;
234                        }
235                        operation |= LOCK_NB;
236                }
237        }
238        if ((ret = flock(fd, operation)) != 0)
239        {
240                switch(errno) {
241                        case EAGAIN:
242                                res = "EAGAIN";
243                                break;
244                        case EBADF:
245                                res = "EBADF";
246                                break;
247                        case EINVAL:
248                                res = "EINVAL";
249                                break;
250                        case EOPNOTSUPP:
251                                res = "EOPNOTSUPP";
252                                break;
253                        default:
254                                res = strerror(errno);
255                                break;
256                }
257                Tcl_SetResult(interp, (void *) res, TCL_STATIC);
258                return TCL_ERROR;
259        }
260        return TCL_OK;
261}
262
263int ReaddirCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
264{
265        DIR *dirp;
266        struct dirent *dp;
267        Tcl_Obj *tcl_result;
268        char *path;
269
270        if (objc != 2) {
271                Tcl_WrongNumArgs(interp, 1, objv, "directory");
272                return TCL_ERROR;
273        }
274
275        path = Tcl_GetString(objv[1]);
276        dirp = opendir(path);
277        if (!dirp) {
278                Tcl_SetResult(interp, "Directory not found", TCL_STATIC);
279                return TCL_ERROR;
280        }
281        tcl_result = Tcl_NewListObj(0, NULL);
282        while ((dp = readdir(dirp))) {
283                Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(dp->d_name, -1));
284        }
285        closedir(dirp);
286        Tcl_SetObjResult(interp, tcl_result);
287       
288        return TCL_OK;
289}
290
291int StrsedCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
292{
293        char *pattern, *string, *res;
294        int range[2];
295        extern char *strsed(char *str, char *pat, int *range);
296
297        if (objc != 3) {
298                Tcl_WrongNumArgs(interp, 1, objv, "string pattern");
299                return TCL_ERROR;
300        }
301
302        string = Tcl_GetString(objv[1]);
303        pattern = Tcl_GetString(objv[2]);
304        res = strsed(string, pattern, range);
305        if (!res) {
306                Tcl_SetResult(interp, "strsed failed", TCL_STATIC);
307                return TCL_ERROR;
308        }
309        Tcl_SetResult(interp, res, (Tcl_FreeProc *)free);
310        return TCL_OK;
311}
312
313int MkstempCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
314{
315        Tcl_Channel channel;
316        ClientData handle;
317        char *template, *channelname;
318        int fd;
319
320        if (objc != 2) {
321                Tcl_WrongNumArgs(interp, 1, objv, "template");
322                return TCL_ERROR;
323        }
324
325        template = strdup(Tcl_GetString(objv[1]));
326        if (template == NULL)
327                return TCL_ERROR;
328
329        if ((fd = mkstemp(template)) < 0) {
330                Tcl_AppendResult(interp, "mkstemp failed: ", strerror(errno), NULL);
331                free(template);
332                return TCL_ERROR;
333        }
334
335        channel = Tcl_MakeFileChannel((ClientData) fd, TCL_READABLE|TCL_WRITABLE);
336        Tcl_RegisterChannel(interp, channel);
337        channelname = Tcl_GetChannelName(channel);
338        Tcl_AppendResult(interp, channelname, " ", template, NULL);
339        free(template);
340        return TCL_OK;
341}
342
343int Pextlib_Init(Tcl_Interp *interp)
344{
345        Tcl_CreateObjCommand(interp, "system", SystemCmd, NULL, NULL);
346        Tcl_CreateObjCommand(interp, "flock", FlockCmd, NULL, NULL);
347        Tcl_CreateObjCommand(interp, "readdir", ReaddirCmd, NULL, NULL);
348        Tcl_CreateObjCommand(interp, "strsed", StrsedCmd, NULL, NULL);
349        Tcl_CreateObjCommand(interp, "mkstemp", MkstempCmd, NULL, NULL);
350        if(Tcl_PkgProvide(interp, "Pextlib", "1.0") != TCL_OK)
351                return TCL_ERROR;
352        return TCL_OK;
353}
Note: See TracBrowser for help on using the repository browser.