Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclUnixFile.c
1810 views
1
/*
2
* tclUnixFile.c --
3
*
4
* This file contains wrappers around UNIX file handling functions.
5
* These wrappers mask differences between Windows and UNIX.
6
*
7
* Copyright (c) 1995 Sun Microsystems, Inc.
8
*
9
* See the file "license.terms" for information on usage and redistribution
10
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
*
12
* SCCS: @(#) tclUnixFile.c 1.41 96/12/05 14:59:20
13
*/
14
15
#include "tclInt.h"
16
#include "tclPort.h"
17
18
/*
19
* The variable below caches the name of the current working directory
20
* in order to avoid repeated calls to getcwd. The string is malloc-ed.
21
* NULL means the cache needs to be refreshed.
22
*/
23
24
static char *currentDir = NULL;
25
static int currentDirExitHandlerSet = 0;
26
27
/*
28
* The variable below is set if the exit routine for deleting the string
29
* containing the executable name has been registered.
30
*/
31
32
static int executableNameExitHandlerSet = 0;
33
34
extern pid_t waitpid _ANSI_ARGS_((pid_t pid, int *stat_loc, int options));
35
36
/*
37
* Static routines for this file:
38
*/
39
40
static void FreeCurrentDir _ANSI_ARGS_((ClientData clientData));
41
static void FreeExecutableName _ANSI_ARGS_((ClientData clientData));
42
43
/*
44
*----------------------------------------------------------------------
45
*
46
* Tcl_WaitPid --
47
*
48
* Implements the waitpid system call on Unix systems.
49
*
50
* Results:
51
* Result of calling waitpid.
52
*
53
* Side effects:
54
* Waits for a process to terminate.
55
*
56
*----------------------------------------------------------------------
57
*/
58
59
int
60
Tcl_WaitPid(pid, statPtr, options)
61
int pid;
62
int *statPtr;
63
int options;
64
{
65
int result;
66
pid_t real_pid;
67
68
real_pid = (pid_t) pid;
69
while (1) {
70
result = (int) waitpid(real_pid, statPtr, options);
71
if ((result != -1) || (errno != EINTR)) {
72
return result;
73
}
74
}
75
}
76
77
/*
78
*----------------------------------------------------------------------
79
*
80
* FreeCurrentDir --
81
*
82
* Frees the string stored in the currentDir variable. This routine
83
* is registered as an exit handler and will be called during shutdown.
84
*
85
* Results:
86
* None.
87
*
88
* Side effects:
89
* Frees the memory occuppied by the currentDir value.
90
*
91
*----------------------------------------------------------------------
92
*/
93
94
/* ARGSUSED */
95
static void
96
FreeCurrentDir(clientData)
97
ClientData clientData; /* Not used. */
98
{
99
if (currentDir != (char *) NULL) {
100
ckfree(currentDir);
101
currentDir = (char *) NULL;
102
}
103
}
104
105
/*
106
*----------------------------------------------------------------------
107
*
108
* FreeExecutableName --
109
*
110
* Frees the string stored in the tclExecutableName variable. This
111
* routine is registered as an exit handler and will be called
112
* during shutdown.
113
*
114
* Results:
115
* None.
116
*
117
* Side effects:
118
* Frees the memory occuppied by the tclExecutableName value.
119
*
120
*----------------------------------------------------------------------
121
*/
122
123
/* ARGSUSED */
124
static void
125
FreeExecutableName(clientData)
126
ClientData clientData; /* Not used. */
127
{
128
if (tclExecutableName != (char *) NULL) {
129
ckfree(tclExecutableName);
130
tclExecutableName = (char *) NULL;
131
}
132
}
133
134
/*
135
*----------------------------------------------------------------------
136
*
137
* TclChdir --
138
*
139
* Change the current working directory.
140
*
141
* Results:
142
* The result is a standard Tcl result. If an error occurs and
143
* interp isn't NULL, an error message is left in interp->result.
144
*
145
* Side effects:
146
* The working directory for this application is changed. Also
147
* the cache maintained used by TclGetCwd is deallocated and
148
* set to NULL.
149
*
150
*----------------------------------------------------------------------
151
*/
152
153
int
154
TclChdir(interp, dirName)
155
Tcl_Interp *interp; /* If non NULL, used for error reporting. */
156
char *dirName; /* Path to new working directory. */
157
{
158
if (currentDir != NULL) {
159
ckfree(currentDir);
160
currentDir = NULL;
161
}
162
if (chdir(dirName) != 0) {
163
if (interp != NULL) {
164
Tcl_AppendResult(interp, "couldn't change working directory to \"",
165
dirName, "\": ", Tcl_PosixError(interp), (char *) NULL);
166
}
167
return TCL_ERROR;
168
}
169
return TCL_OK;
170
}
171
172
/*
173
*----------------------------------------------------------------------
174
*
175
* TclGetCwd --
176
*
177
* Return the path name of the current working directory.
178
*
179
* Results:
180
* The result is the full path name of the current working
181
* directory, or NULL if an error occurred while figuring it out.
182
* The returned string is owned by the TclGetCwd routine and must
183
* not be freed by the caller. If an error occurs and interp
184
* isn't NULL, an error message is left in interp->result.
185
*
186
* Side effects:
187
* The path name is cached to avoid having to recompute it
188
* on future calls; if it is already cached, the cached
189
* value is returned.
190
*
191
*----------------------------------------------------------------------
192
*/
193
194
char *
195
TclGetCwd(interp)
196
Tcl_Interp *interp; /* If non NULL, used for error reporting. */
197
{
198
char buffer[MAXPATHLEN+1];
199
200
if (currentDir == NULL) {
201
if (!currentDirExitHandlerSet) {
202
currentDirExitHandlerSet = 1;
203
Tcl_CreateExitHandler(FreeCurrentDir, (ClientData) NULL);
204
}
205
if (getcwd(buffer, MAXPATHLEN+1) == NULL) {
206
if (interp != NULL) {
207
if (errno == ERANGE) {
208
interp->result = "working directory name is too long";
209
} else {
210
Tcl_AppendResult(interp,
211
"error getting working directory name: ",
212
Tcl_PosixError(interp), (char *) NULL);
213
}
214
}
215
return NULL;
216
}
217
currentDir = (char *) ckalloc((unsigned) (strlen(buffer) + 1));
218
strcpy(currentDir, buffer);
219
}
220
return currentDir;
221
}
222
223
/*
224
*----------------------------------------------------------------------
225
*
226
* TclOpenFile --
227
*
228
* Implements a mechanism to open files on Unix systems.
229
*
230
* Results:
231
* The opened file.
232
*
233
* Side effects:
234
* May cause a file to be created on the file system.
235
*
236
*----------------------------------------------------------------------
237
*/
238
239
Tcl_File
240
TclOpenFile(fname, mode)
241
char *fname; /* The name of the file to open. */
242
int mode; /* In what mode to open the file? */
243
{
244
int fd;
245
246
fd = open(fname, mode, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
247
if (fd != -1) {
248
fcntl(fd, F_SETFD, FD_CLOEXEC);
249
return Tcl_GetFile((ClientData)fd, TCL_UNIX_FD);
250
}
251
return NULL;
252
}
253
254
/*
255
*----------------------------------------------------------------------
256
*
257
* TclCloseFile --
258
*
259
* Implements a mechanism to close a UNIX file.
260
*
261
* Results:
262
* Returns 0 on success, or -1 on error, setting errno.
263
*
264
* Side effects:
265
* The file is closed.
266
*
267
*----------------------------------------------------------------------
268
*/
269
270
int
271
TclCloseFile(file)
272
Tcl_File file; /* The file to close. */
273
{
274
int type;
275
int fd;
276
int result;
277
278
fd = (int) Tcl_GetFileInfo(file, &type);
279
if (type != TCL_UNIX_FD) {
280
panic("Tcl_CloseFile: unexpected file type");
281
}
282
283
/*
284
* Refuse to close the fds for stdin, stdout and stderr.
285
*/
286
287
if ((fd == 0) || (fd == 1) || (fd == 2)) {
288
return 0;
289
}
290
291
result = close(fd);
292
Tcl_DeleteFileHandler(file);
293
Tcl_FreeFile(file);
294
return result;
295
}
296
297
/*
298
*----------------------------------------------------------------------
299
*
300
* TclReadFile --
301
*
302
* Implements a mechanism to read from files on Unix systems. Also
303
* simulates blocking behavior on non-blocking files when asked to.
304
*
305
* Results:
306
* The number of characters read from the specified file.
307
*
308
* Side effects:
309
* May consume characters from the file.
310
*
311
*----------------------------------------------------------------------
312
*/
313
/* ARGSUSED */
314
int
315
TclReadFile(file, shouldBlock, buf, toRead)
316
Tcl_File file; /* The file to read from. */
317
int shouldBlock; /* Not used. */
318
char *buf; /* The buffer to store input in. */
319
int toRead; /* Number of characters to read. */
320
{
321
int type, fd;
322
323
fd = (int) Tcl_GetFileInfo(file, &type);
324
if (type != TCL_UNIX_FD) {
325
panic("Tcl_ReadFile: unexpected file type");
326
}
327
328
return read(fd, buf, (size_t) toRead);
329
}
330
331
/*
332
*----------------------------------------------------------------------
333
*
334
* TclWriteFile --
335
*
336
* Implements a mechanism to write to files on Unix systems.
337
*
338
* Results:
339
* The number of characters written to the specified file.
340
*
341
* Side effects:
342
* May produce characters on the file.
343
*
344
*----------------------------------------------------------------------
345
*/
346
347
/* ARGSUSED */
348
int
349
TclWriteFile(file, shouldBlock, buf, toWrite)
350
Tcl_File file; /* The file to write to. */
351
int shouldBlock; /* Not used. */
352
char *buf; /* Where output is stored. */
353
int toWrite; /* Number of characters to write. */
354
{
355
int type, fd;
356
357
fd = (int) Tcl_GetFileInfo(file, &type);
358
if (type != TCL_UNIX_FD) {
359
panic("Tcl_WriteFile: unexpected file type");
360
}
361
return write(fd, buf, (size_t) toWrite);
362
}
363
364
/*
365
*----------------------------------------------------------------------
366
*
367
* TclSeekFile --
368
*
369
* Sets the file pointer on the indicated UNIX file.
370
*
371
* Results:
372
* The new position at which the file will be accessed, or -1 on
373
* failure.
374
*
375
* Side effects:
376
* May change the position at which subsequent operations access the
377
* file designated by the file.
378
*
379
*----------------------------------------------------------------------
380
*/
381
382
int
383
TclSeekFile(file, offset, whence)
384
Tcl_File file; /* The file to seek on. */
385
int offset; /* How far to seek? */
386
int whence; /* And from where to seek? */
387
{
388
int type, fd;
389
390
fd = (int) Tcl_GetFileInfo(file, &type);
391
if (type != TCL_UNIX_FD) {
392
panic("Tcl_SeekFile: unexpected file type");
393
}
394
395
return lseek(fd, offset, whence);
396
}
397
398
/*
399
*----------------------------------------------------------------------
400
*
401
* TclCreateTempFile --
402
*
403
* This function creates a temporary file initialized with an
404
* optional string, and returns a file handle with the file pointer
405
* at the beginning of the file.
406
*
407
* Results:
408
* A handle to a file.
409
*
410
* Side effects:
411
* None.
412
*
413
*----------------------------------------------------------------------
414
*/
415
416
Tcl_File
417
#ifdef TKSH_V5
418
TclCreateTempFile(contents)
419
#else
420
TclCreateTempFile(contents, namePtr)
421
#endif
422
char *contents; /* String to write into temp file, or NULL. */
423
#ifndef TKSH_V5
424
Tcl_DString *namePtr; /* If non-NULL, pointer to initialized
425
* DString that is filled with the name of
426
* the temp file that was created. */
427
#endif
428
{
429
char fileName[L_tmpnam];
430
Tcl_File file;
431
size_t length = (contents == NULL) ? 0 : strlen(contents);
432
433
tmpnam(fileName);
434
file = TclOpenFile(fileName, O_RDWR|O_CREAT|O_TRUNC);
435
unlink(fileName);
436
437
if ((file != NULL) && (length > 0)) {
438
int fd = (int)Tcl_GetFileInfo(file, NULL);
439
while (1) {
440
if (write(fd, contents, length) != -1) {
441
break;
442
} else if (errno != EINTR) {
443
close(fd);
444
Tcl_FreeFile(file);
445
return NULL;
446
}
447
}
448
lseek(fd, 0, SEEK_SET);
449
}
450
#ifndef TKSH_V5
451
if (namePtr != NULL) {
452
Tcl_DStringAppend(namePtr, fileName, -1);
453
}
454
#endif
455
return file;
456
}
457
458
/*
459
*----------------------------------------------------------------------
460
*
461
* Tcl_FindExecutable --
462
*
463
* This procedure computes the absolute path name of the current
464
* application, given its argv[0] value.
465
*
466
* Results:
467
* None.
468
*
469
* Side effects:
470
* The variable tclExecutableName gets filled in with the file
471
* name for the application, if we figured it out. If we couldn't
472
* figure it out, Tcl_FindExecutable is set to NULL.
473
*
474
*----------------------------------------------------------------------
475
*/
476
477
void
478
Tcl_FindExecutable(argv0)
479
char *argv0; /* The value of the application's argv[0]. */
480
{
481
char *name, *p, *cwd;
482
Tcl_DString buffer;
483
int length;
484
485
Tcl_DStringInit(&buffer);
486
if (tclExecutableName != NULL) {
487
ckfree(tclExecutableName);
488
tclExecutableName = NULL;
489
}
490
491
name = argv0;
492
for (p = name; *p != 0; p++) {
493
if (*p == '/') {
494
/*
495
* The name contains a slash, so use the name directly
496
* without doing a path search.
497
*/
498
499
goto gotName;
500
}
501
}
502
503
p = getenv("PATH");
504
if (p == NULL) {
505
/*
506
* There's no PATH environment variable; use the default that
507
* is used by sh.
508
*/
509
510
p = ":/bin:/usr/bin";
511
}
512
513
/*
514
* Search through all the directories named in the PATH variable
515
* to see if argv[0] is in one of them. If so, use that file
516
* name.
517
*/
518
519
while (*p != 0) {
520
while (isspace(UCHAR(*p))) {
521
p++;
522
}
523
name = p;
524
while ((*p != ':') && (*p != 0)) {
525
p++;
526
}
527
Tcl_DStringSetLength(&buffer, 0);
528
if (p != name) {
529
Tcl_DStringAppend(&buffer, name, p-name);
530
if (p[-1] != '/') {
531
Tcl_DStringAppend(&buffer, "/", 1);
532
}
533
}
534
Tcl_DStringAppend(&buffer, argv0, -1);
535
if (access(Tcl_DStringValue(&buffer), X_OK) == 0) {
536
name = Tcl_DStringValue(&buffer);
537
goto gotName;
538
}
539
p++;
540
}
541
goto done;
542
543
/*
544
* If the name starts with "/" then just copy it to tclExecutableName.
545
*/
546
547
gotName:
548
if (name[0] == '/') {
549
tclExecutableName = (char *) ckalloc((unsigned) (strlen(name) + 1));
550
strcpy(tclExecutableName, name);
551
goto done;
552
}
553
554
/*
555
* The name is relative to the current working directory. First
556
* strip off a leading "./", if any, then add the full path name of
557
* the current working directory.
558
*/
559
560
if ((name[0] == '.') && (name[1] == '/')) {
561
name += 2;
562
}
563
cwd = TclGetCwd((Tcl_Interp *) NULL);
564
if (cwd == NULL) {
565
tclExecutableName = NULL;
566
goto done;
567
}
568
length = strlen(cwd);
569
tclExecutableName = (char *) ckalloc((unsigned)
570
(length + strlen(name) + 2));
571
strcpy(tclExecutableName, cwd);
572
tclExecutableName[length] = '/';
573
strcpy(tclExecutableName + length + 1, name);
574
575
done:
576
Tcl_DStringFree(&buffer);
577
578
if (!executableNameExitHandlerSet) {
579
executableNameExitHandlerSet = 1;
580
Tcl_CreateExitHandler(FreeExecutableName, (ClientData) NULL);
581
}
582
}
583
584
/*
585
*----------------------------------------------------------------------
586
*
587
* TclGetUserHome --
588
*
589
* This function takes the passed in user name and finds the
590
* corresponding home directory specified in the password file.
591
*
592
* Results:
593
* The result is a pointer to a static string containing
594
* the new name. If there was an error in processing the
595
* user name then the return value is NULL. Otherwise the
596
* result is stored in bufferPtr, and the caller must call
597
* Tcl_DStringFree(bufferPtr) to free the result.
598
*
599
* Side effects:
600
* Information may be left in bufferPtr.
601
*
602
*----------------------------------------------------------------------
603
*/
604
605
char *
606
TclGetUserHome(name, bufferPtr)
607
char *name; /* User name to use to find home directory. */
608
Tcl_DString *bufferPtr; /* May be used to hold result. Must not hold
609
* anything at the time of the call, and need
610
* not even be initialized. */
611
{
612
struct passwd *pwPtr;
613
614
pwPtr = getpwnam(name);
615
if (pwPtr == NULL) {
616
endpwent();
617
return NULL;
618
}
619
Tcl_DStringInit(bufferPtr);
620
Tcl_DStringAppend(bufferPtr, pwPtr->pw_dir, -1);
621
endpwent();
622
return bufferPtr->string;
623
}
624
625
#if 0
626
/*
627
*----------------------------------------------------------------------
628
*
629
* TclMatchFiles --
630
*
631
* This routine is used by the globbing code to search a
632
* directory for all files which match a given pattern.
633
*
634
* Results:
635
* If the tail argument is NULL, then the matching files are
636
* added to the interp->result. Otherwise, TclDoGlob is called
637
* recursively for each matching subdirectory. The return value
638
* is a standard Tcl result indicating whether an error occurred
639
* in globbing.
640
*
641
* Side effects:
642
* None.
643
*
644
*---------------------------------------------------------------------- */
645
646
int
647
TclMatchFiles(interp, separators, dirPtr, pattern, tail)
648
Tcl_Interp *interp; /* Interpreter to receive results. */
649
char *separators; /* Path separators to pass to TclDoGlob. */
650
Tcl_DString *dirPtr; /* Contains path to directory to search. */
651
char *pattern; /* Pattern to match against. */
652
char *tail; /* Pointer to end of pattern. */
653
{
654
char *dirName, *patternEnd = tail;
655
char savedChar = 0; /* Initialization needed only to prevent
656
* compiler warning from gcc. */
657
DIR *d;
658
struct stat statBuf;
659
struct dirent *entryPtr;
660
int matchHidden;
661
int result = TCL_OK;
662
int baseLength = Tcl_DStringLength(dirPtr);
663
664
/*
665
* Make sure that the directory part of the name really is a
666
* directory. If the directory name is "", use the name "."
667
* instead, because some UNIX systems don't treat "" like "."
668
* automatically. Keep the "" for use in generating file names,
669
* otherwise "glob foo.c" would return "./foo.c".
670
*/
671
672
if (dirPtr->string[0] == '\0') {
673
dirName = ".";
674
} else {
675
dirName = dirPtr->string;
676
}
677
if ((stat(dirName, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
678
return TCL_OK;
679
}
680
681
/*
682
* Check to see if the pattern needs to compare with hidden files.
683
*/
684
685
if ((pattern[0] == '.')
686
|| ((pattern[0] == '\\') && (pattern[1] == '.'))) {
687
matchHidden = 1;
688
} else {
689
matchHidden = 0;
690
}
691
692
/*
693
* Now open the directory for reading and iterate over the contents.
694
*/
695
696
d = opendir(dirName);
697
if (d == NULL) {
698
Tcl_ResetResult(interp);
699
700
/*
701
* Strip off a trailing '/' if necessary, before reporting the error.
702
*/
703
704
if (baseLength > 0) {
705
savedChar = dirPtr->string[baseLength-1];
706
if (savedChar == '/') {
707
dirPtr->string[baseLength-1] = '\0';
708
}
709
}
710
Tcl_AppendResult(interp, "couldn't read directory \"",
711
dirPtr->string, "\": ", Tcl_PosixError(interp), (char *) NULL);
712
if (baseLength > 0) {
713
dirPtr->string[baseLength-1] = savedChar;
714
}
715
return TCL_ERROR;
716
}
717
718
/*
719
* Clean up the end of the pattern and the tail pointer. Leave
720
* the tail pointing to the first character after the path separator
721
* following the pattern, or NULL. Also, ensure that the pattern
722
* is null-terminated.
723
*/
724
725
if (*tail == '\\') {
726
tail++;
727
}
728
if (*tail == '\0') {
729
tail = NULL;
730
} else {
731
tail++;
732
}
733
savedChar = *patternEnd;
734
*patternEnd = '\0';
735
736
while (1) {
737
entryPtr = readdir(d);
738
if (entryPtr == NULL) {
739
break;
740
}
741
742
/*
743
* Don't match names starting with "." unless the "." is
744
* present in the pattern.
745
*/
746
747
if (!matchHidden && (*entryPtr->d_name == '.')) {
748
continue;
749
}
750
751
/*
752
* Now check to see if the file matches. If there are more
753
* characters to be processed, then ensure matching files are
754
* directories before calling TclDoGlob. Otherwise, just add
755
* the file to the result.
756
*/
757
758
if (Tcl_StringMatch(entryPtr->d_name, pattern)) {
759
Tcl_DStringSetLength(dirPtr, baseLength);
760
Tcl_DStringAppend(dirPtr, entryPtr->d_name, -1);
761
if (tail == NULL) {
762
Tcl_AppendElement(interp, dirPtr->string);
763
} else if ((stat(dirPtr->string, &statBuf) == 0)
764
&& S_ISDIR(statBuf.st_mode)) {
765
Tcl_DStringAppend(dirPtr, "/", 1);
766
result = TclDoGlob(interp, separators, dirPtr, tail);
767
if (result != TCL_OK) {
768
break;
769
}
770
}
771
}
772
}
773
*patternEnd = savedChar;
774
775
closedir(d);
776
return result;
777
}
778
#endif
779
780