Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtSvc / DtCodelibs / filegen.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * File:        filegen.C $TOG: filegen.C /main/7 1999/10/14 15:05:25 mgreess $
25  *
26  * (c) Copyright 1993, 1994 Hewlett-Packard Company
27  * (c) Copyright 1993, 1994 International Business Machines Corp.
28  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.
29  * (c) Copyright 1993, 1994 Novell, Inc.
30  */
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #define X_INCLUDE_DIRENT_H
36 #define XOS_USE_XT_LOCKING
37 #include <X11/Xos_r.h>
38 #include <codelibs/stringx.h>
39 #include <codelibs/nl_hack.h>
40 #include "buf.h"
41 #include "DtSvcLock.h"
42
43 #ifndef MAXPATHLEN
44 # define MAXPATHLEN 1024
45 #endif
46
47 #ifdef XTHREADS
48 extern "C" {
49     extern void XtProcessLock(void);
50     extern void XtProcessUnlock(void);
51 }
52 #endif
53
54 struct _SHXcomponent
55 {
56     boolean is_pattern;
57     long offset;                // subscript in path buffer
58     char *ptr;                  // pointer into path buffer
59 };
60
61 declare_array(_SHXcomponents, _SHXcomponent, 4)
62
63 // recursive routine to expand the wildcard path represented in stack into
64 // all possible expansions.  The expansions are appended to _SHXbuf::vec.
65 // filebuf is a scratch buffer passed in by the caller and is used to build
66 // intermediate paths.  end is a pointer to the position in filebuf where
67 // the calling routine left off.
68 void
69 _SHXbuf::expand(_SHXcomponents &stack,
70         char *const filebuf, char *end, int compnum)
71 {
72     *end = '\0';
73
74     if (compnum == stack.size())
75         return;
76
77     _SHXcomponent &comp = stack[compnum];
78
79     // double-slash?
80     if (comp.ptr[0] == '\0')
81     {
82         if (compnum + 1 == stack.size())
83             append(filebuf, EXPANDQUOTE);
84         else
85         {
86             *end++ = '/';
87             expand(stack, filebuf, end, compnum + 1);
88         }
89         return;
90     }
91
92     // performance optimization:  if this path component
93     // doesn't contain a wildcard, avoid doing an opendir()
94     if (!comp.is_pattern)
95     {
96         strcpy(end, comp.ptr);
97         if (compnum + 1 == stack.size())
98         {
99             // last component, just see if the path really points to something
100             if (access(filebuf, F_OK) != -1)
101                 append(filebuf, EXPANDQUOTE);
102         }
103         else
104         {
105             // intermediate directory just append this component and keep going
106             char *end2 = strend(end);
107             *end2++ = '/';
108             expand(stack, filebuf, end2, compnum + 1);
109         }
110         return;
111     }
112
113     // We have a wildcard component, open and scan its parent directory
114     // and look for matches.
115     DIR *dir = opendir(filebuf[0] == '\0' ? "." : filebuf);
116     if (dir == NULL)
117         return;
118
119     _Xreaddirparams dir_buf;
120     struct dirent *ent;
121
122     memset((char*) &dir_buf, 0, sizeof(_Xreaddirparams));
123     while ((ent = _XReaddir(dir, dir_buf)) != NULL)
124     {
125         // deleted file?
126         if (ent->d_ino == 0 || ent->d_name[0] == '\0')
127             continue;
128
129         // right name?
130         if (comp.is_pattern)
131         {
132             wchar_t __nlh_char[1];
133
134             // Must have explicit match for leading '.'
135             if (CHARAT(ent->d_name) == '.' && CHARAT(comp.ptr) != '.')
136                 continue;
137
138             if (strwcmp(comp.ptr, ent->d_name) != 0)
139                 continue;
140         }
141         else if (strcmp(comp.ptr, ent->d_name) != 0)
142             continue;
143
144         strcpy(end, ent->d_name);
145         if (compnum + 1 == stack.size())
146             append(filebuf, EXPANDQUOTE);
147         else
148         {
149             char *end2 = end + strlen (ent->d_name);
150             *end2++ = '/';
151             expand(stack, filebuf, end2, compnum + 1);
152         }
153     }
154
155     closedir(dir);
156 }
157
158
159 //extern "C" { void qsort(void *, unsigned, int, ...); };
160
161 static char *bufptr;
162
163 static int
164 //compar(int &v1, int &v2)
165 compar(const void *v1, const void *v2)
166 {
167     int result;
168
169     _DtSvcProcessLock();
170     result = strcmp(&bufptr[*(int*)v1], &bufptr[*(int*)v2]);
171     _DtSvcProcessUnlock();
172     return (result);
173 }
174
175 void
176 _SHXbuf::filegen()
177 {
178     privbuf_charbuf path;
179     _SHXcomponents stack;
180
181     long vecstart = vec.size() - 1;
182     if (vecstart < 0)
183         return;
184
185     long bufstart = long(vec[vecstart]);
186
187     // Parse the file path, breaking it up into individual components.
188     // Each component is marked as being either a wildcard component
189     // or not.  The wildcard components will have a '\' placed before
190     // any quoted wildcard characters.  The non-wildcard components
191     // will be left unchanged.
192     int bufpos = (int) bufstart;
193     while (bufpos < buf.size())
194     {
195         _SHXcomponent & comp = stack[stack.size()];
196         comp.is_pattern = FALSE;
197         comp.offset = path.size();
198         comp.ptr = NULL;
199         int startpos = bufpos;
200
201         int ch;
202         do
203         {
204             ch = buf[bufpos];
205             switch (ch)
206             {
207             case '/': 
208                 ch = '\0';
209                 break;
210             case '*': 
211             case '?': 
212             case '[': 
213                 if (flags[bufpos] == NOQUOTE)
214                     comp.is_pattern = TRUE;
215                 else
216                     path[path.size()] = '\\';
217                 break;
218             }
219
220             path[path.size()] = ch;
221             bufpos++;
222         } while (ch != '\0');
223
224         // Add a '*' to the end of the last component if needed
225         // for completion.
226         if (bufpos >= buf.size())   // last component?
227             if (bufpos > bufstart + 1)  // non-null string?
228                 if (completion && !is_pattern)
229                 {
230                     path[path.size() - 1] = '*';
231                     path[path.size()] = '\0';
232                     comp.is_pattern = TRUE;
233                     break;
234                 }
235
236         // If it wasn't a pattern, remove all of the '\' characters
237         // that were added.
238         if (!comp.is_pattern)
239         {
240             int len = bufpos - startpos - 1;
241             strncpy(&path[comp.offset], &buf[startpos], len);
242             path[comp.offset + len] = '\0';
243         }
244     }
245
246     // Fill in the character pointer values for all of the components.
247     // We couldn't do this in the first pass because path is a
248     // dynamic array.
249     char *pathbuf = path.getarr();
250     for (int i = 0; i < stack.size(); i++)
251         stack[i].ptr = &pathbuf[stack[i].offset];
252
253     // Remove the token that we just copied from the return vector
254     // so that we can replace it with its expansion.
255     vec.reset(vecstart);
256
257     char filebuf[MAXPATHLEN];
258     expand(stack, filebuf, filebuf, 0);
259
260     if (vec.size() == vecstart) // no matches?
261     {
262         vec[vecstart] = (char *)bufstart;   // restore orig. token
263         return;
264     }
265
266     // alphabetize the expansion to make it look pretty like ksh does.
267     _DtSvcProcessLock();
268     bufptr = buf.getarr();
269     qsort(&vec[vecstart], (unsigned int)(vec.size() - vecstart),
270             sizeof (char *), compar);
271
272     // Find the longest match if we are doing completion:
273     if (completion)
274     {
275         // compare all entries to a copy of the first entry
276         strcpy(filebuf, &bufptr[long(vec[0])]);
277
278         for (long i = 1; i < vec.size(); i++)
279         {
280             register char *ref = filebuf;
281             register char *ptr = &bufptr[long(vec[i])];
282             while (*ref == *ptr && *ref != '\0' && *ptr != '\0')
283                 ref++, ptr++;
284
285             *ref = '\0';        // shorten the reference copy
286         }
287
288         // Now store the best match as the first token.  We will
289         // have to shift the expansion vector down by one to
290         // make room.
291         for (i = vec.size(); i > 0; --i)
292         {
293             register char *val = vec[i - 1];
294             vec[i] = val;
295         }
296         vec[0] = (char *)buf.size();
297         append(filebuf, EXPANDQUOTE);
298         vec.reset(vec.size() - 1);  // adjust for the append
299     }
300     _DtSvcProcessUnlock();
301 }
302
303 implement_array(_SHXcomponents, _SHXcomponent, 4)