1 /* FILLFNAM.C   (c) Copyright Volker Bandke, 2003-2006               */
2 /*              Hercules filename completion functions               */
3 
4 #include "hstdinc.h"
5 #include "hercules.h"
6 #include "fillfnam.h"
7 
8 /* On Solaris 2.9 (SunOS 5.9) and earlier, there is no scandir
9    and alphasort function. In this case fillfnam does nothing
10    and the tab command is effectively a no-operation */
11 #if !(defined(HAVE_SCANDIR) && defined(HAVE_ALPHASORT)) && !defined(_MSVC_)
tab_pressed(char * cmdlinefull,int * cmdoffset)12 int tab_pressed(char *cmdlinefull, int *cmdoffset) {
13   UNREFERENCED(cmdlinefull);
14   UNREFERENCED(cmdoffset);
15   return 0;
16 }
17 #else
18 
19 char *filterarray;
20 
filter(const struct dirent * ent)21 int filter(const struct dirent *ent) {
22   if (filterarray == NULL)
23     return(1);
24   if (strncmp(ent->d_name, filterarray, strlen(filterarray)) == 0)
25     return(1);
26   return(0);
27 }
28 
29 /* tab can be pressed anywhere in the command line, so I have to split
30    command line
31    for example: attach 0a00 3390 volu_ sf=volume1.shadow
32    will be divided
33    part1 = "attach 0a00 "
34    part2 = "volu"
35    part3 = " sf=volume1.shadow"
36    only part2 can be changed and it must be divided into path and filename
37 */
tab_pressed(char * cmdlinefull,int * cmdoffset)38 int tab_pressed(char *cmdlinefull, int *cmdoffset) {
39   struct dirent **namelist;
40   int n, i, j, len, len1, len2;
41   int cmdoff = *(cmdoffset); /* for easy reading of source code */
42   char *part1, *part2, *part3;
43   char *buff;
44   char *filename, *path, *tmp;
45   char result[1024];
46   char pathname[MAX_PATH];
47 #ifdef _MSVC_
48   int within_quoted_string = 0;
49   int quote_pos;
50 #endif
51 
52   /* part3 goes from cursor position to the end of line */
53   part3 = cmdlinefull + cmdoff;
54   /* looking for ' ','@' or '=' backward, starting from cursor offset
55      I am not aware of any other delimiters which can be used in hercules */
56   /* (except for '"' (double quote) for the MSVC version of herc - Fish) */
57 #ifdef _MSVC_
58   /* determine if tab was pressed within a quoted string */
59   for (i=0; i < cmdoff; i++)
60       if ('\"' == cmdlinefull[i])
61       {
62           within_quoted_string = !within_quoted_string;
63           quote_pos = i; /* (save position of quote
64                          immediately preceding cmdoff) */
65       }
66   if (within_quoted_string)
67     i = quote_pos; /* (the quote is our delimiter) */
68   else
69 #endif
70   for (i = cmdoff-1; i>=0; i--)
71     if (cmdlinefull[i] == ' '
72        || cmdlinefull[i] == '@'
73        || cmdlinefull[i] == '=')
74       break;
75   /* part1 is from beginning to found delimiter (including delimiter itself) */
76   part1 = (char*) malloc(i+2);
77   strncpy(part1, cmdlinefull, i+1);
78   part1[i+1]= '\0';
79 
80   /* part2 is the interesting one */
81   part2 = (char*)malloc(cmdoff - i);
82   strncpy(part2, cmdlinefull + i + 1, cmdoff - i - 1);
83   part2[cmdoff - i - 1] = '\0';
84 
85   len = strlen(part2);
86   /* 3 characters minimum needed, for ./\0 in path. */
87   /* (or 4 chars for MSVC if within quoted string, for \"./\0 - Fish) */
88 #ifdef _MSVC_
89   if (within_quoted_string)
90   {
91     if (len < 3)
92       len = 3;
93   }
94   else
95 #endif
96   if (len < 2)
97     len = 2;
98   path = (char*)malloc(len + 1);
99   *path = '\0';
100   filename = part2;
101   /* is it pure filename or is there whole path ? */
102   tmp = strrchr(part2, '/');
103 #ifdef _MSVC_
104   if (!tmp) tmp = strrchr(part2, '\\');
105 #endif
106   if (tmp != NULL) {
107     filename = tmp + 1;
108     strncpy(path, part2, strlen(part2)-strlen(filename));
109     path[strlen(part2)-strlen(filename)] = '\0';
110     tmp[0] = '\0';
111   }
112   else {
113 #ifdef _MSVC_
114     if (within_quoted_string)
115       strcpy(path,"\"./");
116     else
117 #endif
118     strcpy(path,"./");
119   }
120   /* this is used in filter function to include only relevant filenames */
121   filterarray = filename;
122 
123   n = scandir(path, &namelist, filter, alphasort);
124   if (n > 0) {
125     for (i=0; i<n; i++) {
126       struct stat buf;
127       char fullfilename[1+MAX_PATH+1];
128       /* namelist[i]->d_name contains filtered filenames, check if they are
129          directories with stat(), before that create whole path */
130       if (tmp != NULL)
131          sprintf(fullfilename, "%s%s", path, namelist[i]->d_name);
132       else
133          sprintf(fullfilename, "%s", namelist[i]->d_name);
134 #ifdef _MSVC_
135       if (within_quoted_string)
136         strlcat(fullfilename,"\"",sizeof(fullfilename));
137 #endif
138       /* if it is a directory, add '/' to the end so it can be seen on screen*/
139       hostpath(pathname, fullfilename, sizeof(pathname));
140       if (stat(pathname,&buf) == 0)
141          if (buf.st_mode & S_IFDIR) {
142 //          strcat(namelist[i]->d_name,"/");
143 // Don't write past end of d_name
144 // Problem debugged by bb5ch39t
145             namelist[i] = realloc(namelist[i], sizeof(struct dirent)
146                                 + strlen(namelist[i]->d_name) + 2);
147             if (namelist[i])
148                strcat(namelist[i]->d_name,"/");
149          }
150     }
151     /* now check, if filenames have something in common, after a cycle buff
152        contains maximal intersection of names */
153     buff = (char*)malloc(strlen(namelist[0]->d_name) + 1); /* first one */
154     strcpy(buff, namelist[0]->d_name);
155     for (i = 1; i < n; i++) {
156       len1 = strlen(buff);
157       len2 = strlen(namelist[i]->d_name);
158       /* check number of characters in shorter one */
159       len = len1 > len2 ? len2 : len1;
160       for (j = 0; j < len; j++)
161       if (buff[j] != namelist[i]->d_name[j]) {
162         buff[j] = '\0'; /* end the string where characters are not equal */
163         break;
164       }
165     }
166     /* if maximal intersection of filenames is longer then original filename */
167     if (strlen(buff) > strlen(filename)) {
168       char *fullfilename;
169 
170       fullfilename = (char*)malloc(strlen(path) + strlen(buff) + 1);
171       /* this test is not useless as path contains './' if there was no path
172          in original filename. it is because of scandir function, which
173          needs path portion */
174       if (tmp != NULL)
175          sprintf(fullfilename, "%s%s", path, buff);
176       else
177          sprintf(fullfilename, "%s", buff);
178       /* construct command line */
179       sprintf(result, "%s%s%s", part1, fullfilename, part3);
180       /* move cursor */
181       *(cmdoffset) = strlen(part1) + strlen(fullfilename);
182       strcpy(cmdlinefull, result);
183       free(fullfilename);
184     }
185     else {
186       /* display all alternatives */
187       for (i = 0; i< n; i++)
188          logmsg("%s\n", namelist[i]->d_name);
189     }
190     /* free everything */
191     free(buff);
192     for (i = 0; i< n; i++)
193       free(namelist[i]);
194     free(namelist);
195 
196   }
197   free(part1);
198   free(part2);
199   free(path);
200   return(0);
201 }
202 #endif /*(HAVE_SCANDIR && HAVE_ALPHASORT) || _MSVC_*/
203