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

Last change on this file since 992 was 992, checked in by kevin, 18 years ago

optimized exec cat to fcopy in reinplace
ftruncate(2) original file after performing substitutions
added ftruncate(2) command to Pextlib.c

  • Property svn:eol-style set to native
File size: 9.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
263/* XXX: cannot set eof > 2GB */
264int FtruncateCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
265{
266        int fd, ret;
267        long eof = 0;
268        char *res;
269        Tcl_Channel channel;
270        ClientData handle;
271
272        if (objc > 3) {
273                Tcl_WrongNumArgs(interp, 1, objv, "channelId eof");
274                return TCL_ERROR;
275        }
276
277        if ((channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL)) == NULL)
278        return TCL_ERROR;
279
280        if (Tcl_GetChannelHandle(channel, TCL_READABLE|TCL_WRITABLE, &handle) != TCL_OK) {
281                Tcl_SetResult(interp, "error getting channel handle", TCL_STATIC);
282                return TCL_ERROR;
283        }
284        fd = (int) handle;
285
286        if (Tcl_GetLongFromObj(interp, objv[2], &eof) != TCL_OK) {
287                return TCL_ERROR;
288        }
289
290        if ((ret = ftruncate(fd, eof)) != 0)
291        {
292                switch(errno) {
293                        case EBADF:
294                                res = "EBADF";
295                                break;
296                        case EINVAL:
297                                res = "EINVAL";
298                                break;
299                        default:
300                                res = strerror(errno);
301                                break;
302                }
303                Tcl_SetResult(interp, (void *) res, TCL_STATIC);
304                return TCL_ERROR;
305        }
306        return TCL_OK;
307}
308
309int ReaddirCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
310{
311        DIR *dirp;
312        struct dirent *dp;
313        Tcl_Obj *tcl_result;
314        char *path;
315
316        if (objc != 2) {
317                Tcl_WrongNumArgs(interp, 1, objv, "directory");
318                return TCL_ERROR;
319        }
320
321        path = Tcl_GetString(objv[1]);
322        dirp = opendir(path);
323        if (!dirp) {
324                Tcl_SetResult(interp, "Directory not found", TCL_STATIC);
325                return TCL_ERROR;
326        }
327        tcl_result = Tcl_NewListObj(0, NULL);
328        while ((dp = readdir(dirp))) {
329                Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(dp->d_name, -1));
330        }
331        closedir(dirp);
332        Tcl_SetObjResult(interp, tcl_result);
333       
334        return TCL_OK;
335}
336
337int StrsedCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
338{
339        char *pattern, *string, *res;
340        int range[2];
341        extern char *strsed(char *str, char *pat, int *range);
342
343        if (objc != 3) {
344                Tcl_WrongNumArgs(interp, 1, objv, "string pattern");
345                return TCL_ERROR;
346        }
347
348        string = Tcl_GetString(objv[1]);
349        pattern = Tcl_GetString(objv[2]);
350        res = strsed(string, pattern, range);
351        if (!res) {
352                Tcl_SetResult(interp, "strsed failed", TCL_STATIC);
353                return TCL_ERROR;
354        }
355        Tcl_SetResult(interp, res, (Tcl_FreeProc *)free);
356        return TCL_OK;
357}
358
359int MkstempCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
360{
361        Tcl_Channel channel;
362        ClientData handle;
363        char *template, *channelname;
364        int fd;
365
366        if (objc != 2) {
367                Tcl_WrongNumArgs(interp, 1, objv, "template");
368                return TCL_ERROR;
369        }
370
371        template = strdup(Tcl_GetString(objv[1]));
372        if (template == NULL)
373                return TCL_ERROR;
374
375        if ((fd = mkstemp(template)) < 0) {
376                Tcl_AppendResult(interp, "mkstemp failed: ", strerror(errno), NULL);
377                free(template);
378                return TCL_ERROR;
379        }
380
381        channel = Tcl_MakeFileChannel((ClientData) fd, TCL_READABLE|TCL_WRITABLE);
382        Tcl_RegisterChannel(interp, channel);
383        channelname = Tcl_GetChannelName(channel);
384        Tcl_AppendResult(interp, channelname, " ", template, NULL);
385        free(template);
386        return TCL_OK;
387}
388
389int Pextlib_Init(Tcl_Interp *interp)
390{
391        Tcl_CreateObjCommand(interp, "system", SystemCmd, NULL, NULL);
392        Tcl_CreateObjCommand(interp, "flock", FlockCmd, NULL, NULL);
393        Tcl_CreateObjCommand(interp, "ftruncate", FtruncateCmd, NULL, NULL);
394        Tcl_CreateObjCommand(interp, "readdir", ReaddirCmd, NULL, NULL);
395        Tcl_CreateObjCommand(interp, "strsed", StrsedCmd, NULL, NULL);
396        Tcl_CreateObjCommand(interp, "mkstemp", MkstempCmd, NULL, NULL);
397        if(Tcl_PkgProvide(interp, "Pextlib", "1.0") != TCL_OK)
398                return TCL_ERROR;
399        return TCL_OK;
400}
Note: See TracBrowser for help on using the repository browser.