File: derefsymlink.c

package info (click to toggle)
binstats 1.08-5
  • links: PTS
  • area: main
  • in suites: woody
  • size: 76 kB
  • ctags: 22
  • sloc: sh: 479; ansic: 186; makefile: 38
file content (251 lines) | stat: -rw-r--r-- 6,992 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
 * derefsymlink - a program to dereference symbolic links
 * 
 * Copyright (c) 1996 by Peter Chang
 * Peter.Chang@nottingham.ac.uk
 * 
 * It takes in pathnames and spits them out fully dereference.
 * If any of the components of the pathnames do not exist or
 * contain circular symlinks, the program stays silent or
 * prints a message to stderr.
 * [Some of the error checking is rather paranoid but it's better
 * to be safe than sorry.]
 * 
 * Revision 0.1 created on 1996/8/21
 *  made skeleton program which processes options and filenames
 *  correctly, derefname() just echos back at the moment
 * Revision 0.2 written on 1996/8/23
 *  filled in rest of program, including derefname() and striprel()
 *  added verbose flag
 * Revision 0.3 debugged on 1997/12/22
 *  the last test in derefname() did not cope with a single link to
 *  a top directory
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>

#define IN_USE_ARGV  0
#define IN_USE_FILE  1
#define IN_USE_STDIN 2
#define OUT_USE_STDOUT 0
#define OUT_USE_FILE   1
#define DEPTH_LIMIT 25 /* max directory depth */

int in_method = IN_USE_ARGV;
int out_method = OUT_USE_STDOUT;
int verbose = 0;

int main(int argc, char *argv[])
{
   static char iname[FILENAME_MAX], oname[FILENAME_MAX],
     cname[FILENAME_MAX];
   int al, i;
   FILE *fi, *fo;

   int pcl(int , char **, char *, char *);
   int derefname(char *, int );

   /* look for options */
   al = pcl(argc, argv, iname, oname);

   /* set up output */
   if (out_method == OUT_USE_FILE) {
      if ((fo = fopen(oname, "w")) == NULL) {
	 if (verbose) fprintf(stderr, "can't open output file %s", oname);
	 exit(EXIT_FAILURE);
      }
   } else fo = stdout;

   /* if there is a list from a file or stdin
    * then go through this first */
   if (in_method != IN_USE_ARGV) {
      if (in_method == IN_USE_FILE) {
	 if ((fi = fopen(iname, "r")) == NULL) {
	    if (verbose) fprintf(stderr, "can't open input file %s", iname);
	    exit(EXIT_FAILURE);
	 }
      } else fi = stdin;
      while ((i = fscanf(fi, "%s", cname)) != EOF) {
	 if (derefname(cname, 0) >= 0) fprintf(fo, "%s\n", cname);
      }
   }

   /* now finish off the remaining arguments */
   while (al < argc) {
      strncpy(cname, argv[al++], FILENAME_MAX-1);
      if (derefname(cname, 0) >= 0) fprintf(fo, "%s\n", cname);
   }

   if (out_method == OUT_USE_FILE) fclose(fo);
   
   return EXIT_SUCCESS;
}


static char curdir[FILENAME_MAX];

/* do the real work here */
int derefname(char *name, int depth)
{
   char tempa[FILENAME_MAX], cname[FILENAME_MAX];
   struct stat sbuf;
   int len, sc;
   char *ptra;

   int striprel(char *);

   /* check if we are bombing out */
   if (depth < 0) return depth;
   if (depth > DEPTH_LIMIT) {
      /* recursion limit to prevent circular reference */
      if (verbose) fprintf(stderr, "circular symbolic link: %s\n", name);
      return -1;
   }
   strncpy(cname, name, FILENAME_MAX-1);
   if (name[0] != '/') { /* is name absolute */
      if (curdir[0] == '\0') { /* initialise current directory */
	 getcwd(curdir, FILENAME_MAX-1);
	 strncat(curdir, "/", FILENAME_MAX-1);
      }
      /* append cwd to local name */
      strncpy(cname, curdir, FILENAME_MAX-1);
      strncat(cname, name, FILENAME_MAX-1);
   }
   if (striprel(cname) != 0) return -1;
   if (lstat(cname, &sbuf) != 0) {
      if (verbose) fprintf(stderr, "%s does not exist\n", cname);
      return -1;
   }
   sc = 0;
   while (S_ISLNK(sbuf.st_mode)) { /* check for symlink and deref it */
      if (sc > DEPTH_LIMIT) {
	 /* recursion limit to prevent circular reference */
	 if (verbose) fprintf(stderr, "circular symbolic link: %s\n", cname);
	 return -1;
      }
      len = readlink(cname, tempa, FILENAME_MAX-1);
      tempa[len] = '\0';
      if (tempa[0] != '/') {
	 if ((ptra = strrchr(cname, '/')) == NULL) {
	    if (verbose) fprintf(stderr, "Error in stripping link\n");
	    return -1;
	 }
	 *(ptra+1) = '\0';
	 strncat(cname, tempa, FILENAME_MAX-1);
      } else {
	 strncpy(cname, tempa, FILENAME_MAX-1);
      }
      if (striprel(cname) != 0) return -1;
      if (lstat(cname, &sbuf) != 0) {
	 if (verbose) fprintf(stderr, "%s does not exist\n", cname);
	 return -1;
      }
      sc++;
   }

   /* strip last name */
   if ((ptra = strrchr(cname, '/')) == NULL) {
      if (verbose) fprintf(stderr, "Error in stripping name\n");
      return -1;
   }
   if (ptra != cname) {
      strncpy(tempa, ptra, FILENAME_MAX-1);
      *ptra = '\0';
      /* dereference rest of path */
      depth = derefname(cname, depth+1);
      strncpy(name, cname, FILENAME_MAX-1);
      strncat(name, tempa, FILENAME_MAX-1);
   } else strncpy(name, cname, FILENAME_MAX-1);

   return depth;
}


/* remove all "/../" and "/./" in pathnames */
int striprel(char *name)
{
   static char tempa[FILENAME_MAX], tempb[FILENAME_MAX];
   char *ptra, *ptrb;

   strncpy(tempa, name, FILENAME_MAX-1);
   while ((ptra = strstr(tempa, "/../")) != NULL) {
      *ptra = '\0';
      if ((ptrb = strrchr(tempa, '/')) == NULL) {
	 if (verbose) fprintf(stderr, "Error in stripping '..'\n");
	 return -1;
      }
      *(ptrb+1) = '\0';
      strncpy(tempb, ptra+4, FILENAME_MAX-1);
      strncat(tempa, tempb, FILENAME_MAX-1);
   }

   while ((ptra = strstr(tempa, "/./")) != NULL) {
      *ptra = '\0';
      strncpy(tempb, ptra+2, FILENAME_MAX-1);
      strncat(tempa, tempb, FILENAME_MAX-1);
   }
   strncpy(name, tempa, FILENAME_MAX-1);   
   return 0;
}


/*
 * Parse the command line set the filename and the appropriate flags
 */
int pcl(int argc, char **argv, char *iname, char *oname)
{
   static const char *options[] = {
      "help                    print this message",
      "file <list>        use a list of filenames",
      "output <file>   use file instead of stdout",
      "stdin                   use stdin as input",
      "verbose               print error messages",
      NULL
   };
   static const char usage0[] = "usage: derefsymlink [options] [filename] ...",
     usage1[] = " try derefsymlink -help for a list of options";
   int i, optno, olen;
   const char **sptr;

   for (i = 1; i < argc; i++) {
      olen = strlen(argv[i])-1;
      if (argv[i][0] == '-') {
	 for (sptr = options, optno = 0; *sptr != NULL; sptr++, optno++)
	   if (strncmp(argv[i]+1, *sptr, olen) == 0) break;

	 switch(optno) {
	  case 0:
	    printf("%s\n", usage0);
	    printf("Options:\n");
	    for (sptr = options; *sptr != NULL; sptr++)
	      printf(" -%s\n", *sptr);
	    printf("\n");
	    exit(EXIT_SUCCESS);
	    break;
	  case 1:
	    in_method = IN_USE_FILE;
	    strcpy(iname, argv[++i]);
	    break;
	  case 2:
	    out_method = OUT_USE_FILE;
	    strcpy(oname, argv[++i]);
	    break;
	  case 3:
	    in_method = IN_USE_STDIN;
	    break;
	  case 4:
	    verbose = 1;
	    break;
	  default:
	    fprintf(stderr, "%s\n%s\n", usage0, usage1);
	    exit(EXIT_FAILURE);
	    break;
	 }
      } else return i;
   }
   return i;
}