/[dtapublic]/projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclfcmd.c
ViewVC logotype

Diff of /projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclfcmd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 64 by dashley, Sun Oct 30 04:21:11 2016 UTC revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
1  /* $Header$ */  /* $Header$ */
2  /*  /*
3   * tclFCmd.c   * tclFCmd.c
4   *   *
5   *      This file implements the generic portion of file manipulation   *      This file implements the generic portion of file manipulation
6   *      subcommands of the "file" command.   *      subcommands of the "file" command.
7   *   *
8   * Copyright (c) 1996-1998 Sun Microsystems, Inc.   * Copyright (c) 1996-1998 Sun Microsystems, Inc.
9   *   *
10   * See the file "license.terms" for information on usage and redistribution   * See the file "license.terms" for information on usage and redistribution
11   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12   *   *
13   * RCS: @(#) $Id: tclfcmd.c,v 1.1.1.1 2001/06/13 04:39:01 dtashley Exp $   * RCS: @(#) $Id: tclfcmd.c,v 1.1.1.1 2001/06/13 04:39:01 dtashley Exp $
14   */   */
15    
16  #include "tclInt.h"  #include "tclInt.h"
17  #include "tclPort.h"  #include "tclPort.h"
18    
19  /*  /*
20   * Declarations for local procedures defined in this file:   * Declarations for local procedures defined in this file:
21   */   */
22    
23  static int              CopyRenameOneFile _ANSI_ARGS_((Tcl_Interp *interp,  static int              CopyRenameOneFile _ANSI_ARGS_((Tcl_Interp *interp,
24                              char *source, char *dest, int copyFlag,                              char *source, char *dest, int copyFlag,
25                              int force));                              int force));
26  static char *           FileBasename _ANSI_ARGS_((Tcl_Interp *interp,  static char *           FileBasename _ANSI_ARGS_((Tcl_Interp *interp,
27                              char *path, Tcl_DString *bufferPtr));                              char *path, Tcl_DString *bufferPtr));
28  static int              FileCopyRename _ANSI_ARGS_((Tcl_Interp *interp,  static int              FileCopyRename _ANSI_ARGS_((Tcl_Interp *interp,
29                              int argc, char **argv, int copyFlag));                              int argc, char **argv, int copyFlag));
30  static int              FileForceOption _ANSI_ARGS_((Tcl_Interp *interp,  static int              FileForceOption _ANSI_ARGS_((Tcl_Interp *interp,
31                              int argc, char **argv, int *forcePtr));                              int argc, char **argv, int *forcePtr));
32    
33  /*  /*
34   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
35   *   *
36   * TclFileRenameCmd   * TclFileRenameCmd
37   *   *
38   *      This procedure implements the "rename" subcommand of the "file"   *      This procedure implements the "rename" subcommand of the "file"
39   *      command.  Filename arguments need to be translated to native   *      command.  Filename arguments need to be translated to native
40   *      format before being passed to platform-specific code that   *      format before being passed to platform-specific code that
41   *      implements rename functionality.   *      implements rename functionality.
42   *   *
43   * Results:   * Results:
44   *      A standard Tcl result.   *      A standard Tcl result.
45   *   *
46   * Side effects:   * Side effects:
47   *      See the user documentation.   *      See the user documentation.
48   *   *
49   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
50   */   */
51    
52  int  int
53  TclFileRenameCmd(interp, argc, argv)  TclFileRenameCmd(interp, argc, argv)
54      Tcl_Interp *interp;         /* Interp for error reporting. */      Tcl_Interp *interp;         /* Interp for error reporting. */
55      int argc;                   /* Number of arguments. */      int argc;                   /* Number of arguments. */
56      char **argv;                /* Argument strings passed to Tcl_FileCmd. */      char **argv;                /* Argument strings passed to Tcl_FileCmd. */
57  {  {
58      return FileCopyRename(interp, argc, argv, 0);      return FileCopyRename(interp, argc, argv, 0);
59  }  }
60    
61  /*  /*
62   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
63   *   *
64   * TclFileCopyCmd   * TclFileCopyCmd
65   *   *
66   *      This procedure implements the "copy" subcommand of the "file"   *      This procedure implements the "copy" subcommand of the "file"
67   *      command.  Filename arguments need to be translated to native   *      command.  Filename arguments need to be translated to native
68   *      format before being passed to platform-specific code that   *      format before being passed to platform-specific code that
69   *      implements copy functionality.   *      implements copy functionality.
70   *   *
71   * Results:   * Results:
72   *      A standard Tcl result.   *      A standard Tcl result.
73   *   *
74   * Side effects:   * Side effects:
75   *      See the user documentation.   *      See the user documentation.
76   *   *
77   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
78   */   */
79    
80  int  int
81  TclFileCopyCmd(interp, argc, argv)  TclFileCopyCmd(interp, argc, argv)
82      Tcl_Interp *interp;         /* Used for error reporting */      Tcl_Interp *interp;         /* Used for error reporting */
83      int argc;                   /* Number of arguments. */      int argc;                   /* Number of arguments. */
84      char **argv;                /* Argument strings passed to Tcl_FileCmd. */      char **argv;                /* Argument strings passed to Tcl_FileCmd. */
85  {  {
86      return FileCopyRename(interp, argc, argv, 1);      return FileCopyRename(interp, argc, argv, 1);
87  }  }
88    
89  /*  /*
90   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
91   *   *
92   * FileCopyRename --   * FileCopyRename --
93   *   *
94   *      Performs the work of TclFileRenameCmd and TclFileCopyCmd.   *      Performs the work of TclFileRenameCmd and TclFileCopyCmd.
95   *      See comments for those procedures.   *      See comments for those procedures.
96   *   *
97   * Results:   * Results:
98   *      See above.   *      See above.
99   *   *
100   * Side effects:   * Side effects:
101   *      See above.   *      See above.
102   *   *
103   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
104   */   */
105    
106  static int  static int
107  FileCopyRename(interp, argc, argv, copyFlag)  FileCopyRename(interp, argc, argv, copyFlag)
108      Tcl_Interp *interp;         /* Used for error reporting. */      Tcl_Interp *interp;         /* Used for error reporting. */
109      int argc;                   /* Number of arguments. */      int argc;                   /* Number of arguments. */
110      char **argv;                /* Argument strings passed to Tcl_FileCmd. */      char **argv;                /* Argument strings passed to Tcl_FileCmd. */
111      int copyFlag;               /* If non-zero, copy source(s).  Otherwise,      int copyFlag;               /* If non-zero, copy source(s).  Otherwise,
112                                   * rename them. */                                   * rename them. */
113  {  {
114      int i, result, force;      int i, result, force;
115      struct stat statBuf;      struct stat statBuf;
116      Tcl_DString targetBuffer;      Tcl_DString targetBuffer;
117      char *target;      char *target;
118    
119      i = FileForceOption(interp, argc - 2, argv + 2, &force);      i = FileForceOption(interp, argc - 2, argv + 2, &force);
120      if (i < 0) {      if (i < 0) {
121          return TCL_ERROR;          return TCL_ERROR;
122      }      }
123      i += 2;      i += 2;
124      if ((argc - i) < 2) {      if ((argc - i) < 2) {
125          Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],          Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
126                  " ", argv[1], " ?options? source ?source ...? target\"",                  " ", argv[1], " ?options? source ?source ...? target\"",
127                  (char *) NULL);                  (char *) NULL);
128          return TCL_ERROR;          return TCL_ERROR;
129      }      }
130    
131      /*      /*
132       * If target doesn't exist or isn't a directory, try the copy/rename.       * If target doesn't exist or isn't a directory, try the copy/rename.
133       * More than 2 arguments is only valid if the target is an existing       * More than 2 arguments is only valid if the target is an existing
134       * directory.       * directory.
135       */       */
136    
137      target = Tcl_TranslateFileName(interp, argv[argc - 1], &targetBuffer);      target = Tcl_TranslateFileName(interp, argv[argc - 1], &targetBuffer);
138      if (target == NULL) {      if (target == NULL) {
139          return TCL_ERROR;          return TCL_ERROR;
140      }      }
141    
142      result = TCL_OK;      result = TCL_OK;
143    
144      /*      /*
145       * Call TclStat() so that if target is a symlink that points to a       * Call TclStat() so that if target is a symlink that points to a
146       * directory we will put the sources in that directory instead of       * directory we will put the sources in that directory instead of
147       * overwriting the symlink.       * overwriting the symlink.
148       */       */
149    
150      if ((TclStat(target, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {      if ((TclStat(target, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
151          if ((argc - i) > 2) {          if ((argc - i) > 2) {
152              errno = ENOTDIR;              errno = ENOTDIR;
153              Tcl_PosixError(interp);              Tcl_PosixError(interp);
154              Tcl_AppendResult(interp, "error ",              Tcl_AppendResult(interp, "error ",
155                      ((copyFlag) ? "copying" : "renaming"), ": target \"",                      ((copyFlag) ? "copying" : "renaming"), ": target \"",
156                      argv[argc - 1], "\" is not a directory", (char *) NULL);                      argv[argc - 1], "\" is not a directory", (char *) NULL);
157              result = TCL_ERROR;              result = TCL_ERROR;
158          } else {          } else {
159              /*              /*
160               * Even though already have target == translated(argv[i+1]),               * Even though already have target == translated(argv[i+1]),
161               * pass the original argument down, so if there's an error, the               * pass the original argument down, so if there's an error, the
162               * error message will reflect the original arguments.               * error message will reflect the original arguments.
163               */               */
164    
165              result = CopyRenameOneFile(interp, argv[i], argv[i + 1], copyFlag,              result = CopyRenameOneFile(interp, argv[i], argv[i + 1], copyFlag,
166                      force);                      force);
167          }          }
168          Tcl_DStringFree(&targetBuffer);          Tcl_DStringFree(&targetBuffer);
169          return result;          return result;
170      }      }
171            
172      /*      /*
173       * Move each source file into target directory.  Extract the basename       * Move each source file into target directory.  Extract the basename
174       * from each source, and append it to the end of the target path.       * from each source, and append it to the end of the target path.
175       */       */
176    
177      for ( ; i < argc - 1; i++) {      for ( ; i < argc - 1; i++) {
178          char *jargv[2];          char *jargv[2];
179          char *source, *newFileName;          char *source, *newFileName;
180          Tcl_DString sourceBuffer, newFileNameBuffer;          Tcl_DString sourceBuffer, newFileNameBuffer;
181    
182          source = FileBasename(interp, argv[i], &sourceBuffer);          source = FileBasename(interp, argv[i], &sourceBuffer);
183          if (source == NULL) {          if (source == NULL) {
184              result = TCL_ERROR;              result = TCL_ERROR;
185              break;              break;
186          }          }
187          jargv[0] = argv[argc - 1];          jargv[0] = argv[argc - 1];
188          jargv[1] = source;          jargv[1] = source;
189          Tcl_DStringInit(&newFileNameBuffer);          Tcl_DStringInit(&newFileNameBuffer);
190          newFileName = Tcl_JoinPath(2, jargv, &newFileNameBuffer);          newFileName = Tcl_JoinPath(2, jargv, &newFileNameBuffer);
191          result = CopyRenameOneFile(interp, argv[i], newFileName, copyFlag,          result = CopyRenameOneFile(interp, argv[i], newFileName, copyFlag,
192                  force);                  force);
193          Tcl_DStringFree(&sourceBuffer);          Tcl_DStringFree(&sourceBuffer);
194          Tcl_DStringFree(&newFileNameBuffer);          Tcl_DStringFree(&newFileNameBuffer);
195    
196          if (result == TCL_ERROR) {          if (result == TCL_ERROR) {
197              break;              break;
198          }          }
199      }      }
200      Tcl_DStringFree(&targetBuffer);      Tcl_DStringFree(&targetBuffer);
201      return result;      return result;
202  }  }
203    
204  /*  /*
205   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
206   *   *
207   * TclFileMakeDirsCmd   * TclFileMakeDirsCmd
208   *   *
209   *      This procedure implements the "mkdir" subcommand of the "file"   *      This procedure implements the "mkdir" subcommand of the "file"
210   *      command.  Filename arguments need to be translated to native   *      command.  Filename arguments need to be translated to native
211   *      format before being passed to platform-specific code that   *      format before being passed to platform-specific code that
212   *      implements mkdir functionality.   *      implements mkdir functionality.
213   *   *
214   * Results:   * Results:
215   *      A standard Tcl result.   *      A standard Tcl result.
216   *   *
217   * Side effects:   * Side effects:
218   *      See the user documentation.   *      See the user documentation.
219   *   *
220   *----------------------------------------------------------------------   *----------------------------------------------------------------------
221   */   */
222  int  int
223  TclFileMakeDirsCmd(interp, argc, argv)  TclFileMakeDirsCmd(interp, argc, argv)
224      Tcl_Interp *interp;         /* Used for error reporting. */      Tcl_Interp *interp;         /* Used for error reporting. */
225      int argc;                   /* Number of arguments */      int argc;                   /* Number of arguments */
226      char **argv;                /* Argument strings passed to Tcl_FileCmd. */      char **argv;                /* Argument strings passed to Tcl_FileCmd. */
227  {  {
228      Tcl_DString nameBuffer, targetBuffer;      Tcl_DString nameBuffer, targetBuffer;
229      char *errfile;      char *errfile;
230      int result, i, j, pargc;      int result, i, j, pargc;
231      char **pargv;      char **pargv;
232      struct stat statBuf;      struct stat statBuf;
233    
234      pargv = NULL;      pargv = NULL;
235      errfile = NULL;      errfile = NULL;
236      Tcl_DStringInit(&nameBuffer);      Tcl_DStringInit(&nameBuffer);
237      Tcl_DStringInit(&targetBuffer);      Tcl_DStringInit(&targetBuffer);
238    
239      result = TCL_OK;      result = TCL_OK;
240      for (i = 2; i < argc; i++) {      for (i = 2; i < argc; i++) {
241          char *name = Tcl_TranslateFileName(interp, argv[i], &nameBuffer);          char *name = Tcl_TranslateFileName(interp, argv[i], &nameBuffer);
242          if (name == NULL) {          if (name == NULL) {
243              result = TCL_ERROR;              result = TCL_ERROR;
244              break;              break;
245          }          }
246    
247          Tcl_SplitPath(name, &pargc, &pargv);          Tcl_SplitPath(name, &pargc, &pargv);
248          if (pargc == 0) {          if (pargc == 0) {
249              errno = ENOENT;              errno = ENOENT;
250              errfile = argv[i];              errfile = argv[i];
251              break;              break;
252          }          }
253          for (j = 0; j < pargc; j++) {          for (j = 0; j < pargc; j++) {
254              char *target = Tcl_JoinPath(j + 1, pargv, &targetBuffer);              char *target = Tcl_JoinPath(j + 1, pargv, &targetBuffer);
255    
256              /*              /*
257               * Call TclStat() so that if target is a symlink that points               * Call TclStat() so that if target is a symlink that points
258               * to a directory we will create subdirectories in that               * to a directory we will create subdirectories in that
259               * directory.               * directory.
260               */               */
261    
262              if (TclStat(target, &statBuf) == 0) {              if (TclStat(target, &statBuf) == 0) {
263                  if (!S_ISDIR(statBuf.st_mode)) {                  if (!S_ISDIR(statBuf.st_mode)) {
264                      errno = EEXIST;                      errno = EEXIST;
265                      errfile = target;                      errfile = target;
266                      goto done;                      goto done;
267                  }                  }
268              } else if ((errno != ENOENT)              } else if ((errno != ENOENT)
269                      || (TclpCreateDirectory(target) != TCL_OK)) {                      || (TclpCreateDirectory(target) != TCL_OK)) {
270                  errfile = target;                  errfile = target;
271                  goto done;                  goto done;
272              }              }
273              Tcl_DStringFree(&targetBuffer);              Tcl_DStringFree(&targetBuffer);
274          }          }
275          ckfree((char *) pargv);          ckfree((char *) pargv);
276          pargv = NULL;          pargv = NULL;
277          Tcl_DStringFree(&nameBuffer);          Tcl_DStringFree(&nameBuffer);
278      }      }
279                    
280      done:      done:
281      if (errfile != NULL) {      if (errfile != NULL) {
282          Tcl_AppendResult(interp, "can't create directory \"",          Tcl_AppendResult(interp, "can't create directory \"",
283                  errfile, "\": ", Tcl_PosixError(interp), (char *) NULL);                  errfile, "\": ", Tcl_PosixError(interp), (char *) NULL);
284          result = TCL_ERROR;          result = TCL_ERROR;
285      }      }
286    
287      Tcl_DStringFree(&nameBuffer);      Tcl_DStringFree(&nameBuffer);
288      Tcl_DStringFree(&targetBuffer);      Tcl_DStringFree(&targetBuffer);
289      if (pargv != NULL) {      if (pargv != NULL) {
290          ckfree((char *) pargv);          ckfree((char *) pargv);
291      }      }
292      return result;      return result;
293  }  }
294    
295  /*  /*
296   *----------------------------------------------------------------------   *----------------------------------------------------------------------
297   *   *
298   * TclFileDeleteCmd   * TclFileDeleteCmd
299   *   *
300   *      This procedure implements the "delete" subcommand of the "file"   *      This procedure implements the "delete" subcommand of the "file"
301   *      command.   *      command.
302   *   *
303   * Results:   * Results:
304   *      A standard Tcl result.   *      A standard Tcl result.
305   *   *
306   * Side effects:   * Side effects:
307   *      See the user documentation.   *      See the user documentation.
308   *   *
309   *----------------------------------------------------------------------   *----------------------------------------------------------------------
310   */   */
311    
312  int  int
313  TclFileDeleteCmd(interp, argc, argv)  TclFileDeleteCmd(interp, argc, argv)
314      Tcl_Interp *interp;         /* Used for error reporting */      Tcl_Interp *interp;         /* Used for error reporting */
315      int argc;                   /* Number of arguments */      int argc;                   /* Number of arguments */
316      char **argv;                /* Argument strings passed to Tcl_FileCmd. */      char **argv;                /* Argument strings passed to Tcl_FileCmd. */
317  {  {
318      Tcl_DString nameBuffer, errorBuffer;      Tcl_DString nameBuffer, errorBuffer;
319      int i, force, result;      int i, force, result;
320      char *errfile;      char *errfile;
321            
322      i = FileForceOption(interp, argc - 2, argv + 2, &force);      i = FileForceOption(interp, argc - 2, argv + 2, &force);
323      if (i < 0) {      if (i < 0) {
324          return TCL_ERROR;          return TCL_ERROR;
325      }      }
326      i += 2;      i += 2;
327      if ((argc - i) < 1) {      if ((argc - i) < 1) {
328          Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],          Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
329                  " ", argv[1], " ?options? file ?file ...?\"", (char *) NULL);                  " ", argv[1], " ?options? file ?file ...?\"", (char *) NULL);
330          return TCL_ERROR;          return TCL_ERROR;
331      }      }
332    
333      errfile = NULL;      errfile = NULL;
334      result = TCL_OK;      result = TCL_OK;
335      Tcl_DStringInit(&errorBuffer);      Tcl_DStringInit(&errorBuffer);
336      Tcl_DStringInit(&nameBuffer);      Tcl_DStringInit(&nameBuffer);
337    
338      for ( ; i < argc; i++) {      for ( ; i < argc; i++) {
339          struct stat statBuf;          struct stat statBuf;
340          char *name;          char *name;
341    
342          errfile = argv[i];          errfile = argv[i];
343          Tcl_DStringSetLength(&nameBuffer, 0);          Tcl_DStringSetLength(&nameBuffer, 0);
344          name = Tcl_TranslateFileName(interp, argv[i], &nameBuffer);          name = Tcl_TranslateFileName(interp, argv[i], &nameBuffer);
345          if (name == NULL) {          if (name == NULL) {
346              result = TCL_ERROR;              result = TCL_ERROR;
347              goto done;              goto done;
348          }          }
349    
350          /*          /*
351           * Call lstat() to get info so can delete symbolic link itself.           * Call lstat() to get info so can delete symbolic link itself.
352           */           */
353    
354          if (TclpLstat(name, &statBuf) != 0) {          if (TclpLstat(name, &statBuf) != 0) {
355              /*              /*
356               * Trying to delete a file that does not exist is not               * Trying to delete a file that does not exist is not
357               * considered an error, just a no-op               * considered an error, just a no-op
358               */               */
359    
360              if (errno != ENOENT) {              if (errno != ENOENT) {
361                  result = TCL_ERROR;                  result = TCL_ERROR;
362              }              }
363          } else if (S_ISDIR(statBuf.st_mode)) {          } else if (S_ISDIR(statBuf.st_mode)) {
364              result = TclpRemoveDirectory(name, force, &errorBuffer);              result = TclpRemoveDirectory(name, force, &errorBuffer);
365              if (result != TCL_OK) {              if (result != TCL_OK) {
366                  if ((force == 0) && (errno == EEXIST)) {                  if ((force == 0) && (errno == EEXIST)) {
367                      Tcl_AppendResult(interp, "error deleting \"", argv[i],                      Tcl_AppendResult(interp, "error deleting \"", argv[i],
368                              "\": directory not empty", (char *) NULL);                              "\": directory not empty", (char *) NULL);
369                      Tcl_PosixError(interp);                      Tcl_PosixError(interp);
370                      goto done;                      goto done;
371                  }                  }
372    
373                  /*                  /*
374                   * If possible, use the untranslated name for the file.                   * If possible, use the untranslated name for the file.
375                   */                   */
376                                    
377                  errfile = Tcl_DStringValue(&errorBuffer);                  errfile = Tcl_DStringValue(&errorBuffer);
378                  if (strcmp(name, errfile) == 0) {                  if (strcmp(name, errfile) == 0) {
379                      errfile = argv[i];                      errfile = argv[i];
380                  }                  }
381              }              }
382          } else {          } else {
383              result = TclpDeleteFile(name);              result = TclpDeleteFile(name);
384          }          }
385                    
386          if (result == TCL_ERROR) {          if (result == TCL_ERROR) {
387              break;              break;
388          }          }
389      }      }
390      if (result != TCL_OK) {      if (result != TCL_OK) {
391          Tcl_AppendResult(interp, "error deleting \"", errfile,          Tcl_AppendResult(interp, "error deleting \"", errfile,
392                  "\": ", Tcl_PosixError(interp), (char *) NULL);                  "\": ", Tcl_PosixError(interp), (char *) NULL);
393      }      }
394      done:      done:
395      Tcl_DStringFree(&errorBuffer);      Tcl_DStringFree(&errorBuffer);
396      Tcl_DStringFree(&nameBuffer);      Tcl_DStringFree(&nameBuffer);
397      return result;      return result;
398  }  }
399    
400  /*  /*
401   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
402   *   *
403   * CopyRenameOneFile   * CopyRenameOneFile
404   *   *
405   *      Copies or renames specified source file or directory hierarchy   *      Copies or renames specified source file or directory hierarchy
406   *      to the specified target.     *      to the specified target.  
407   *   *
408   * Results:   * Results:
409   *      A standard Tcl result.   *      A standard Tcl result.
410   *   *
411   * Side effects:   * Side effects:
412   *      Target is overwritten if the force flag is set.  Attempting to   *      Target is overwritten if the force flag is set.  Attempting to
413   *      copy/rename a file onto a directory or a directory onto a file   *      copy/rename a file onto a directory or a directory onto a file
414   *      will always result in an error.     *      will always result in an error.  
415   *   *
416   *----------------------------------------------------------------------   *----------------------------------------------------------------------
417   */   */
418    
419  static int  static int
420  CopyRenameOneFile(interp, source, target, copyFlag, force)  CopyRenameOneFile(interp, source, target, copyFlag, force)
421      Tcl_Interp *interp;         /* Used for error reporting. */      Tcl_Interp *interp;         /* Used for error reporting. */
422      char *source;               /* Pathname of file to copy.  May need to      char *source;               /* Pathname of file to copy.  May need to
423                                   * be translated. */                                   * be translated. */
424      char *target;               /* Pathname of file to create/overwrite.      char *target;               /* Pathname of file to create/overwrite.
425                                   * May need to be translated. */                                   * May need to be translated. */
426      int copyFlag;               /* If non-zero, copy files.  Otherwise,      int copyFlag;               /* If non-zero, copy files.  Otherwise,
427                                   * rename them. */                                   * rename them. */
428      int force;                  /* If non-zero, overwrite target file if it      int force;                  /* If non-zero, overwrite target file if it
429                                   * exists.  Otherwise, error if target already                                   * exists.  Otherwise, error if target already
430                                   * exists. */                                   * exists. */
431  {  {
432      int result;      int result;
433      Tcl_DString sourcePath, targetPath, errorBuffer;      Tcl_DString sourcePath, targetPath, errorBuffer;
434      char *targetName, *sourceName, *errfile;      char *targetName, *sourceName, *errfile;
435      struct stat sourceStatBuf, targetStatBuf;      struct stat sourceStatBuf, targetStatBuf;
436    
437      sourceName = Tcl_TranslateFileName(interp, source, &sourcePath);      sourceName = Tcl_TranslateFileName(interp, source, &sourcePath);
438      if (sourceName == NULL) {      if (sourceName == NULL) {
439          return TCL_ERROR;          return TCL_ERROR;
440      }      }
441      targetName = Tcl_TranslateFileName(interp, target, &targetPath);      targetName = Tcl_TranslateFileName(interp, target, &targetPath);
442      if (targetName == NULL) {      if (targetName == NULL) {
443          Tcl_DStringFree(&sourcePath);          Tcl_DStringFree(&sourcePath);
444          return TCL_ERROR;          return TCL_ERROR;
445      }      }
446            
447      errfile = NULL;      errfile = NULL;
448      result = TCL_ERROR;      result = TCL_ERROR;
449      Tcl_DStringInit(&errorBuffer);      Tcl_DStringInit(&errorBuffer);
450            
451      /*      /*
452       * We want to copy/rename links and not the files they point to, so we       * We want to copy/rename links and not the files they point to, so we
453       * use lstat(). If target is a link, we also want to replace the       * use lstat(). If target is a link, we also want to replace the
454       * link and not the file it points to, so we also use lstat() on the       * link and not the file it points to, so we also use lstat() on the
455       * target.       * target.
456       */       */
457    
458      if (TclpLstat(sourceName, &sourceStatBuf) != 0) {      if (TclpLstat(sourceName, &sourceStatBuf) != 0) {
459          errfile = source;          errfile = source;
460          goto done;          goto done;
461      }      }
462      if (TclpLstat(targetName, &targetStatBuf) != 0) {      if (TclpLstat(targetName, &targetStatBuf) != 0) {
463          if (errno != ENOENT) {          if (errno != ENOENT) {
464              errfile = target;              errfile = target;
465              goto done;              goto done;
466          }          }
467      } else {      } else {
468          if (force == 0) {          if (force == 0) {
469              errno = EEXIST;              errno = EEXIST;
470              errfile = target;              errfile = target;
471              goto done;              goto done;
472          }          }
473    
474          /*          /*
475           * Prevent copying or renaming a file onto itself.  Under Windows,           * Prevent copying or renaming a file onto itself.  Under Windows,
476           * stat always returns 0 for st_ino.  However, the Windows-specific           * stat always returns 0 for st_ino.  However, the Windows-specific
477           * code knows how to deal with copying or renaming a file on top of           * code knows how to deal with copying or renaming a file on top of
478           * itself.  It might be a good idea to write a stat that worked.           * itself.  It might be a good idea to write a stat that worked.
479           */           */
480            
481          if ((sourceStatBuf.st_ino != 0) && (targetStatBuf.st_ino != 0)) {          if ((sourceStatBuf.st_ino != 0) && (targetStatBuf.st_ino != 0)) {
482              if ((sourceStatBuf.st_ino == targetStatBuf.st_ino) &&              if ((sourceStatBuf.st_ino == targetStatBuf.st_ino) &&
483                      (sourceStatBuf.st_dev == targetStatBuf.st_dev)) {                      (sourceStatBuf.st_dev == targetStatBuf.st_dev)) {
484                  result = TCL_OK;                  result = TCL_OK;
485                  goto done;                  goto done;
486              }              }
487          }          }
488    
489          /*          /*
490           * Prevent copying/renaming a file onto a directory and           * Prevent copying/renaming a file onto a directory and
491           * vice-versa.  This is a policy decision based on the fact that           * vice-versa.  This is a policy decision based on the fact that
492           * existing implementations of copy and rename on all platforms           * existing implementations of copy and rename on all platforms
493           * also prevent this.           * also prevent this.
494           */           */
495    
496          if (S_ISDIR(sourceStatBuf.st_mode)          if (S_ISDIR(sourceStatBuf.st_mode)
497                  && !S_ISDIR(targetStatBuf.st_mode)) {                  && !S_ISDIR(targetStatBuf.st_mode)) {
498              errno = EISDIR;              errno = EISDIR;
499              Tcl_AppendResult(interp, "can't overwrite file \"", target,              Tcl_AppendResult(interp, "can't overwrite file \"", target,
500                      "\" with directory \"", source, "\"", (char *) NULL);                      "\" with directory \"", source, "\"", (char *) NULL);
501              goto done;              goto done;
502          }          }
503          if (!S_ISDIR(sourceStatBuf.st_mode)          if (!S_ISDIR(sourceStatBuf.st_mode)
504                  && S_ISDIR(targetStatBuf.st_mode)) {                  && S_ISDIR(targetStatBuf.st_mode)) {
505              errno = EISDIR;              errno = EISDIR;
506              Tcl_AppendResult(interp, "can't overwrite directory \"", target,              Tcl_AppendResult(interp, "can't overwrite directory \"", target,
507                      "\" with file \"", source, "\"", (char *) NULL);                      "\" with file \"", source, "\"", (char *) NULL);
508              goto done;              goto done;
509          }          }
510      }      }
511    
512      if (copyFlag == 0) {      if (copyFlag == 0) {
513          result = TclpRenameFile(sourceName, targetName);          result = TclpRenameFile(sourceName, targetName);
514          if (result == TCL_OK) {          if (result == TCL_OK) {
515              goto done;              goto done;
516          }          }
517                            
518          if (errno == EINVAL) {          if (errno == EINVAL) {
519              Tcl_AppendResult(interp, "error renaming \"", source, "\" to \"",              Tcl_AppendResult(interp, "error renaming \"", source, "\" to \"",
520                      target, "\": trying to rename a volume or ",                      target, "\": trying to rename a volume or ",
521                      "move a directory into itself", (char *) NULL);                      "move a directory into itself", (char *) NULL);
522              goto done;              goto done;
523          } else if (errno != EXDEV) {          } else if (errno != EXDEV) {
524              errfile = target;              errfile = target;
525              goto done;              goto done;
526          }          }
527                    
528          /*          /*
529           * The rename failed because the move was across file systems.           * The rename failed because the move was across file systems.
530           * Fall through to copy file and then remove original.  Note that           * Fall through to copy file and then remove original.  Note that
531           * the low-level TclpRenameFile is allowed to implement           * the low-level TclpRenameFile is allowed to implement
532           * cross-filesystem moves itself.           * cross-filesystem moves itself.
533           */           */
534      }      }
535    
536      if (S_ISDIR(sourceStatBuf.st_mode)) {      if (S_ISDIR(sourceStatBuf.st_mode)) {
537          result = TclpCopyDirectory(sourceName, targetName, &errorBuffer);          result = TclpCopyDirectory(sourceName, targetName, &errorBuffer);
538          if (result != TCL_OK) {          if (result != TCL_OK) {
539              errfile = Tcl_DStringValue(&errorBuffer);              errfile = Tcl_DStringValue(&errorBuffer);
540              if (strcmp(errfile, sourceName) == 0) {              if (strcmp(errfile, sourceName) == 0) {
541                  errfile = source;                  errfile = source;
542              } else if (strcmp(errfile, targetName) == 0) {              } else if (strcmp(errfile, targetName) == 0) {
543                  errfile = target;                  errfile = target;
544              }              }
545          }          }
546      } else {      } else {
547          result = TclpCopyFile(sourceName, targetName);          result = TclpCopyFile(sourceName, targetName);
548          if (result != TCL_OK) {          if (result != TCL_OK) {
549              /*              /*
550               * Well, there really shouldn't be a problem with source,               * Well, there really shouldn't be a problem with source,
551               * because up there we checked to see if it was ok to copy it.               * because up there we checked to see if it was ok to copy it.
552               */               */
553    
554              errfile = target;              errfile = target;
555          }          }
556      }      }
557      if ((copyFlag == 0) && (result == TCL_OK)) {      if ((copyFlag == 0) && (result == TCL_OK)) {
558          if (S_ISDIR(sourceStatBuf.st_mode)) {          if (S_ISDIR(sourceStatBuf.st_mode)) {
559              result = TclpRemoveDirectory(sourceName, 1, &errorBuffer);              result = TclpRemoveDirectory(sourceName, 1, &errorBuffer);
560              if (result != TCL_OK) {              if (result != TCL_OK) {
561                  errfile = Tcl_DStringValue(&errorBuffer);                  errfile = Tcl_DStringValue(&errorBuffer);
562                  if (strcmp(errfile, sourceName) == 0) {                  if (strcmp(errfile, sourceName) == 0) {
563                      errfile = source;                      errfile = source;
564                  }                  }
565              }              }
566          } else {          } else {
567              result = TclpDeleteFile(sourceName);              result = TclpDeleteFile(sourceName);
568              if (result != TCL_OK) {              if (result != TCL_OK) {
569                  errfile = source;                  errfile = source;
570              }              }
571          }          }
572          if (result != TCL_OK) {          if (result != TCL_OK) {
573              Tcl_AppendResult(interp, "can't unlink \"", errfile, "\": ",              Tcl_AppendResult(interp, "can't unlink \"", errfile, "\": ",
574                      Tcl_PosixError(interp), (char *) NULL);                      Tcl_PosixError(interp), (char *) NULL);
575              errfile = NULL;              errfile = NULL;
576          }          }
577      }      }
578            
579      done:      done:
580      if (errfile != NULL) {      if (errfile != NULL) {
581          Tcl_AppendResult(interp,          Tcl_AppendResult(interp,
582                  ((copyFlag) ? "error copying \"" : "error renaming \""),                  ((copyFlag) ? "error copying \"" : "error renaming \""),
583                  source, (char *) NULL);                  source, (char *) NULL);
584          if (errfile != source) {          if (errfile != source) {
585              Tcl_AppendResult(interp, "\" to \"", target, (char *) NULL);              Tcl_AppendResult(interp, "\" to \"", target, (char *) NULL);
586              if (errfile != target) {              if (errfile != target) {
587                  Tcl_AppendResult(interp, "\": \"", errfile, (char *) NULL);                  Tcl_AppendResult(interp, "\": \"", errfile, (char *) NULL);
588              }              }
589          }          }
590          Tcl_AppendResult(interp, "\": ", Tcl_PosixError(interp),          Tcl_AppendResult(interp, "\": ", Tcl_PosixError(interp),
591                  (char *) NULL);                  (char *) NULL);
592      }      }
593      Tcl_DStringFree(&errorBuffer);      Tcl_DStringFree(&errorBuffer);
594      Tcl_DStringFree(&sourcePath);      Tcl_DStringFree(&sourcePath);
595      Tcl_DStringFree(&targetPath);      Tcl_DStringFree(&targetPath);
596      return result;      return result;
597  }  }
598    
599  /*  /*
600   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
601   *   *
602   * FileForceOption --   * FileForceOption --
603   *   *
604   *      Helps parse command line options for file commands that take   *      Helps parse command line options for file commands that take
605   *      the "-force" and "--" options.   *      the "-force" and "--" options.
606   *   *
607   * Results:   * Results:
608   *      The return value is how many arguments from argv were consumed   *      The return value is how many arguments from argv were consumed
609   *      by this function, or -1 if there was an error parsing the   *      by this function, or -1 if there was an error parsing the
610   *      options.  If an error occurred, an error message is left in the   *      options.  If an error occurred, an error message is left in the
611   *      interp's result.   *      interp's result.
612   *   *
613   * Side effects:   * Side effects:
614   *      None.   *      None.
615   *   *
616   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
617   */   */
618    
619  static int  static int
620  FileForceOption(interp, argc, argv, forcePtr)  FileForceOption(interp, argc, argv, forcePtr)
621      Tcl_Interp *interp;         /* Interp, for error return. */      Tcl_Interp *interp;         /* Interp, for error return. */
622      int argc;                   /* Number of arguments. */      int argc;                   /* Number of arguments. */
623      char **argv;                /* Argument strings.  First command line      char **argv;                /* Argument strings.  First command line
624                                   * option, if it exists, begins at 0. */                                   * option, if it exists, begins at 0. */
625      int *forcePtr;              /* If the "-force" was specified, *forcePtr      int *forcePtr;              /* If the "-force" was specified, *forcePtr
626                                   * is filled with 1, otherwise with 0. */                                   * is filled with 1, otherwise with 0. */
627  {  {
628      int force, i;      int force, i;
629            
630      force = 0;      force = 0;
631      for (i = 0; i < argc; i++) {      for (i = 0; i < argc; i++) {
632          if (argv[i][0] != '-') {          if (argv[i][0] != '-') {
633              break;              break;
634          }          }
635          if (strcmp(argv[i], "-force") == 0) {          if (strcmp(argv[i], "-force") == 0) {
636              force = 1;              force = 1;
637          } else if (strcmp(argv[i], "--") == 0) {          } else if (strcmp(argv[i], "--") == 0) {
638              i++;              i++;
639              break;              break;
640          } else {          } else {
641              Tcl_AppendResult(interp, "bad option \"", argv[i],              Tcl_AppendResult(interp, "bad option \"", argv[i],
642                      "\": should be -force or --", (char *)NULL);                      "\": should be -force or --", (char *)NULL);
643              return -1;              return -1;
644          }          }
645      }      }
646      *forcePtr = force;      *forcePtr = force;
647      return i;      return i;
648  }  }
649  /*  /*
650   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
651   *   *
652   * FileBasename --   * FileBasename --
653   *   *
654   *      Given a path in either tcl format (with / separators), or in the   *      Given a path in either tcl format (with / separators), or in the
655   *      platform-specific format for the current platform, return all the   *      platform-specific format for the current platform, return all the
656   *      characters in the path after the last directory separator.  But,   *      characters in the path after the last directory separator.  But,
657   *      if path is the root directory, returns no characters.   *      if path is the root directory, returns no characters.
658   *   *
659   * Results:   * Results:
660   *      Appends the string that represents the basename to the end of   *      Appends the string that represents the basename to the end of
661   *      the specified initialized DString, returning a pointer to the   *      the specified initialized DString, returning a pointer to the
662   *      resulting string.  If there is an error, an error message is left   *      resulting string.  If there is an error, an error message is left
663   *      in interp, NULL is returned, and the Tcl_DString is unmodified.   *      in interp, NULL is returned, and the Tcl_DString is unmodified.
664   *   *
665   * Side effects:   * Side effects:
666   *      None.   *      None.
667   *   *
668   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
669   */   */
670    
671  static char *  static char *
672  FileBasename(interp, path, bufferPtr)  FileBasename(interp, path, bufferPtr)
673      Tcl_Interp *interp;         /* Interp, for error return. */      Tcl_Interp *interp;         /* Interp, for error return. */
674      char *path;                 /* Path whose basename to extract. */      char *path;                 /* Path whose basename to extract. */
675      Tcl_DString *bufferPtr;     /* Initialized DString that receives      Tcl_DString *bufferPtr;     /* Initialized DString that receives
676                                   * basename. */                                   * basename. */
677  {  {
678      int argc;      int argc;
679      char **argv;      char **argv;
680            
681      Tcl_SplitPath(path, &argc, &argv);      Tcl_SplitPath(path, &argc, &argv);
682      if (argc == 0) {      if (argc == 0) {
683          Tcl_DStringInit(bufferPtr);          Tcl_DStringInit(bufferPtr);
684      } else {      } else {
685          if ((argc == 1) && (*path == '~')) {          if ((argc == 1) && (*path == '~')) {
686              Tcl_DString buffer;              Tcl_DString buffer;
687                            
688              ckfree((char *) argv);              ckfree((char *) argv);
689              path = Tcl_TranslateFileName(interp, path, &buffer);              path = Tcl_TranslateFileName(interp, path, &buffer);
690              if (path == NULL) {              if (path == NULL) {
691                  return NULL;                  return NULL;
692              }              }
693              Tcl_SplitPath(path, &argc, &argv);              Tcl_SplitPath(path, &argc, &argv);
694              Tcl_DStringFree(&buffer);              Tcl_DStringFree(&buffer);
695          }          }
696          Tcl_DStringInit(bufferPtr);          Tcl_DStringInit(bufferPtr);
697    
698          /*          /*
699           * Return the last component, unless it is the only component, and it           * Return the last component, unless it is the only component, and it
700           * is the root of an absolute path.           * is the root of an absolute path.
701           */           */
702    
703          if (argc > 0) {          if (argc > 0) {
704              if ((argc > 1)              if ((argc > 1)
705                      || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) {                      || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) {
706                  Tcl_DStringAppend(bufferPtr, argv[argc - 1], -1);                  Tcl_DStringAppend(bufferPtr, argv[argc - 1], -1);
707              }              }
708          }          }
709      }      }
710      ckfree((char *) argv);      ckfree((char *) argv);
711      return Tcl_DStringValue(bufferPtr);      return Tcl_DStringValue(bufferPtr);
712  }  }
713    
714  /*  /*
715   *----------------------------------------------------------------------   *----------------------------------------------------------------------
716   *   *
717   * TclFileAttrsCmd --   * TclFileAttrsCmd --
718   *   *
719   *      Sets or gets the platform-specific attributes of a file. The objc-objv   *      Sets or gets the platform-specific attributes of a file. The objc-objv
720   *      points to the file name with the rest of the command line following.   *      points to the file name with the rest of the command line following.
721   *      This routine uses platform-specific tables of option strings   *      This routine uses platform-specific tables of option strings
722   *      and callbacks. The callback to get the attributes take three   *      and callbacks. The callback to get the attributes take three
723   *      parameters:   *      parameters:
724   *          Tcl_Interp *interp;     The interp to report errors with.   *          Tcl_Interp *interp;     The interp to report errors with.
725   *                                  Since this is an object-based API,   *                                  Since this is an object-based API,
726   *                                  the object form of the result should be   *                                  the object form of the result should be
727   *                                  used.   *                                  used.
728   *          CONST char *fileName;   This is extracted using   *          CONST char *fileName;   This is extracted using
729   *                                  Tcl_TranslateFileName.   *                                  Tcl_TranslateFileName.
730   *          TclObj **attrObjPtrPtr; A new object to hold the attribute   *          TclObj **attrObjPtrPtr; A new object to hold the attribute
731   *                                  is allocated and put here.   *                                  is allocated and put here.
732   *      The first two parameters of the callback used to write out the   *      The first two parameters of the callback used to write out the
733   *      attributes are the same. The third parameter is:   *      attributes are the same. The third parameter is:
734   *          CONST *attrObjPtr;      A pointer to the object that has   *          CONST *attrObjPtr;      A pointer to the object that has
735   *                                  the new attribute.   *                                  the new attribute.
736   *      They both return standard TCL errors; if the routine to get   *      They both return standard TCL errors; if the routine to get
737   *      an attribute fails, no object is allocated and *attrObjPtrPtr   *      an attribute fails, no object is allocated and *attrObjPtrPtr
738   *      is unchanged.   *      is unchanged.
739   *   *
740   * Results:   * Results:
741   *      Standard TCL error.   *      Standard TCL error.
742   *   *
743   * Side effects:   * Side effects:
744   *      May set file attributes for the file name.   *      May set file attributes for the file name.
745   *         *      
746   *----------------------------------------------------------------------   *----------------------------------------------------------------------
747   */   */
748    
749  int  int
750  TclFileAttrsCmd(interp, objc, objv)  TclFileAttrsCmd(interp, objc, objv)
751      Tcl_Interp *interp;         /* The interpreter for error reporting. */      Tcl_Interp *interp;         /* The interpreter for error reporting. */
752      int objc;                   /* Number of command line arguments. */      int objc;                   /* Number of command line arguments. */
753      Tcl_Obj *CONST objv[];      /* The command line objects. */      Tcl_Obj *CONST objv[];      /* The command line objects. */
754  {  {
755      char *fileName;      char *fileName;
756      int result;      int result;
757      Tcl_DString buffer;      Tcl_DString buffer;
758    
759      if (objc < 3) {      if (objc < 3) {
760          Tcl_WrongNumArgs(interp, 2, objv,          Tcl_WrongNumArgs(interp, 2, objv,
761                  "name ?option? ?value? ?option value ...?");                  "name ?option? ?value? ?option value ...?");
762          return TCL_ERROR;          return TCL_ERROR;
763      }      }
764    
765      fileName = Tcl_GetString(objv[2]);      fileName = Tcl_GetString(objv[2]);
766      fileName = Tcl_TranslateFileName(interp, fileName, &buffer);      fileName = Tcl_TranslateFileName(interp, fileName, &buffer);
767      if (fileName == NULL) {      if (fileName == NULL) {
768          return TCL_ERROR;          return TCL_ERROR;
769      }      }
770            
771      objc -= 3;      objc -= 3;
772      objv += 3;      objv += 3;
773      result = TCL_ERROR;      result = TCL_ERROR;
774    
775      if (objc == 0) {      if (objc == 0) {
776          /*          /*
777           * Get all attributes.           * Get all attributes.
778           */           */
779    
780          int index;          int index;
781          Tcl_Obj *listPtr, *objPtr;          Tcl_Obj *listPtr, *objPtr;
782                    
783          listPtr = Tcl_NewListObj(0, NULL);          listPtr = Tcl_NewListObj(0, NULL);
784          for (index = 0; tclpFileAttrStrings[index] != NULL; index++) {          for (index = 0; tclpFileAttrStrings[index] != NULL; index++) {
785              objPtr = Tcl_NewStringObj(tclpFileAttrStrings[index], -1);              objPtr = Tcl_NewStringObj(tclpFileAttrStrings[index], -1);
786              Tcl_ListObjAppendElement(interp, listPtr, objPtr);              Tcl_ListObjAppendElement(interp, listPtr, objPtr);
787    
788              if ((*tclpFileAttrProcs[index].getProc)(interp, index, fileName,              if ((*tclpFileAttrProcs[index].getProc)(interp, index, fileName,
789                      &objPtr) != TCL_OK) {                      &objPtr) != TCL_OK) {
790                  Tcl_DecrRefCount(listPtr);                  Tcl_DecrRefCount(listPtr);
791                  goto end;                  goto end;
792              }              }
793              Tcl_ListObjAppendElement(interp, listPtr, objPtr);              Tcl_ListObjAppendElement(interp, listPtr, objPtr);
794          }          }
795          Tcl_SetObjResult(interp, listPtr);          Tcl_SetObjResult(interp, listPtr);
796      } else if (objc == 1) {      } else if (objc == 1) {
797          /*          /*
798           * Get one attribute.           * Get one attribute.
799           */           */
800    
801          int index;          int index;
802          Tcl_Obj *objPtr;          Tcl_Obj *objPtr;
803                    
804          if (Tcl_GetIndexFromObj(interp, objv[0], tclpFileAttrStrings,          if (Tcl_GetIndexFromObj(interp, objv[0], tclpFileAttrStrings,
805                  "option", 0, &index) != TCL_OK) {                  "option", 0, &index) != TCL_OK) {
806              goto end;              goto end;
807          }          }
808          if ((*tclpFileAttrProcs[index].getProc)(interp, index, fileName,          if ((*tclpFileAttrProcs[index].getProc)(interp, index, fileName,
809                  &objPtr) != TCL_OK) {                  &objPtr) != TCL_OK) {
810              goto end;              goto end;
811          }          }
812          Tcl_SetObjResult(interp, objPtr);          Tcl_SetObjResult(interp, objPtr);
813      } else {      } else {
814          /*          /*
815           * Set option/value pairs.           * Set option/value pairs.
816           */           */
817    
818          int i, index;          int i, index;
819                    
820          for (i = 0; i < objc ; i += 2) {          for (i = 0; i < objc ; i += 2) {
821              if (Tcl_GetIndexFromObj(interp, objv[i], tclpFileAttrStrings,              if (Tcl_GetIndexFromObj(interp, objv[i], tclpFileAttrStrings,
822                      "option", 0, &index) != TCL_OK) {                      "option", 0, &index) != TCL_OK) {
823                  goto end;                  goto end;
824              }              }
825              if (i + 1 == objc) {              if (i + 1 == objc) {
826                  Tcl_AppendResult(interp, "value for \"",                  Tcl_AppendResult(interp, "value for \"",
827                          Tcl_GetString(objv[i]), "\" missing",                          Tcl_GetString(objv[i]), "\" missing",
828                          (char *) NULL);                          (char *) NULL);
829                  goto end;                  goto end;
830              }              }
831              if ((*tclpFileAttrProcs[index].setProc)(interp, index, fileName,              if ((*tclpFileAttrProcs[index].setProc)(interp, index, fileName,
832                      objv[i + 1]) != TCL_OK) {                      objv[i + 1]) != TCL_OK) {
833                  goto end;                  goto end;
834              }              }
835          }          }
836      }      }
837      result = TCL_OK;      result = TCL_OK;
838    
839      end:      end:
840      Tcl_DStringFree(&buffer);      Tcl_DStringFree(&buffer);
841      return result;      return result;
842  }  }
843    
844  /* End of tclfcmd.c */  /* End of tclfcmd.c */

Legend:
Removed from v.64  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25