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