2 * ion/share/ion-completefile/ion-completefile.c
5 /****************************************************************************/
7 /* Copyright 1992 Simmule Turner and Rich Salz. All rights reserved. */
9 /* This software is not subject to any license of the American Telephone */
10 /* and Telegraph Company or of the Regents of the University of California. */
12 /* Permission is granted to anyone to use this software for any purpose on */
13 /* any computer system, and to alter it and redistribute it freely, subject */
14 /* to the following restrictions: */
15 /* 1. The authors are not responsible for the consequences of use of this */
16 /* software, no matter how awful, even if they arise from flaws in it. */
17 /* 2. The origin of this software must not be misrepresented, either by */
18 /* explicit claim or by omission. Since few users ever read sources, */
19 /* credits must appear in the documentation. */
20 /* 3. Altered versions must be plainly marked as such, and must not be */
21 /* misrepresented as being the original software. Since few users */
22 /* ever read sources, credits must appear in the documentation. */
23 /* 4. This notice may not be removed or altered. */
25 /****************************************************************************/
27 /* This is a line-editing library, it can be linked into almost any */
28 /* program to provide command-line editing and recall. */
30 /* Posted to comp.sources.misc Sun, 2 Aug 1992 03:05:27 GMT */
31 /* by rsalz@osf.org (Rich $alz) */
33 /****************************************************************************/
35 /* The version contained here has some modifications by awb@cstr.ed.ac.uk */
36 /* (Alan W Black) in order to integrate it with the Edinburgh Speech Tools */
37 /* library and Scheme-in-one-defun in particular. All modifications to */
38 /* to this work are continued with the same copyright above. That is */
39 /* This version editline does not have the the "no commercial use" */
40 /* restriction that some of the rest of the EST library may have */
43 /****************************************************************************/
46 ** History and file completion functions for editline library.
51 * Adapted for use with Ion and tilde-expansion added by
52 * Tuomo Valkonen <tuomov@cc.tut.fi>, 2000-08-23.
55 #include <sys/param.h>
56 #include <sys/types.h>
64 /* needed for NGROUPS_MAX on Solaris 10 */
67 #include <libtu/util.h>
68 #include <libtu/types.h>
69 #include <libtu/misc.h>
70 #include <libtu/output.h>
72 #define STRDUP(X) scopy(X)
73 #define DISPOSE(X) free(X)
74 #define NEW(T, X) ALLOC_N(T, X)
78 #define COPYFROMTO(new, p, len) \
79 (void)memcpy((char *)(new), (char *)(p), (int)(len))
82 /* Hopefully one of these is defined... */
83 #define NGROUPS NGROUPS_MAX
86 typedef struct dirent DIRENTRY
;
88 static int el_is_directory(char *path
)
92 if ((stat(path
, &sb
) >= 0) && S_ISDIR(sb
.st_mode
))
98 static int el_is_executable(char *path
)
100 unsigned int t_uid
= geteuid();
101 gid_t t_gids
[NGROUPS
];
106 groupcount
= getgroups(NGROUPS
, t_gids
);
108 if((stat(path
, &sb
) >= 0) && S_ISREG(sb
.st_mode
)) {
109 /* Normal file, see if we can execute it. */
111 if (sb
.st_mode
& S_IXOTH
) { /* All can execute */
114 if (sb
.st_uid
== t_uid
&& (sb
.st_mode
& S_IXUSR
)) {
117 if (sb
.st_mode
& S_IXGRP
) {
118 for (i
= 0; i
< groupcount
; i
++) {
119 if (sb
.st_gid
== t_gids
[i
]) {
129 ** Fill in *avp with an array of names that match file, up to its length.
132 static int FindMatches(char *dir
,char *file
,char ***avp
)
145 if ((dp
= opendir(dir
)) == NULL
)
151 while ((ep
= readdir(dp
)) != NULL
) {
153 if (p
[0] == '.' && (p
[1] == '\0' || (p
[1] == '.' && p
[2] == '\0')))
155 if (len
&& strncmp(p
, file
, len
) != 0)
158 if ((ac
% MEM_INC
) == 0) {
159 if ((new = NEW(char*, ac
+ MEM_INC
)) == NULL
)
162 COPYFROMTO(new, av
, ac
* sizeof (char **));
168 if ((av
[ac
] = STRDUP(p
)) == NULL
) {
176 /* Clean up and return. */
179 qsort(av, ac, sizeof (char **), compare);*/
185 ** Slight modification on FindMatches, to search through current PATH
188 static int FindFullPathMatches(char *file
,char ***avp
)
193 char *path
= getenv("PATH");
202 t_path
= strdup(path
);
208 t_t_path
= strtok(t_path
, ":\n\0");
210 if ((dp
= opendir(t_t_path
)) == NULL
) {
211 t_t_path
= strtok(NULL
, ":\n\0");
216 while ((ep
= readdir(dp
)) != NULL
) {
218 if (p
[0] == '.' && (p
[1] == '\0' || (p
[0] == '.' &&
223 if (len
&& strncmp(p
, file
, len
) != 0) {
227 snprintf(t_tmp
, 1024, "%s/%s", t_t_path
, p
);
228 if(!el_is_executable(t_tmp
)) {
232 if ((ac
% MEM_INC
) == 0) {
233 if ((new = NEW(char*, ac
+ MEM_INC
)) == NULL
) {
237 COPYFROMTO(new, av
, ac
* sizeof (char **));
242 if ((av
[ac
] = STRDUP(p
)) == NULL
) {
250 t_t_path
= strtok(NULL
, ":\n\0");
255 /* Clean up and return. */
258 qsort(av, ac, sizeof (char **), compare); */
266 ** Split a pathname into allocated directory and trailing filename parts.
268 STATIC
int SplitPath(const char *path
,char **dirpart
,char **filepart
)
270 static char DOT
[] = "./";
274 if ((fpart
= strrchr(path
, '/')) == NULL
) {
275 /* No slashes in path */
276 if ((dpart
= STRDUP(DOT
)) == NULL
)
278 if ((fpart
= STRDUP(path
)) == NULL
) {
283 if ((dpart
= STRDUP(path
)) == NULL
)
285 /* Include the slash -- Tuomo */
286 dpart
[fpart
- path
+ 1] = '\0';
287 if ((fpart
= STRDUP(++fpart
)) == NULL
) {
291 /* Root no longer a special case due above -- Tuomo
292 if (dpart[0] == '\0')
305 ** Split a pathname into allocated directory and trailing filename parts.
307 STATIC
int SplitRelativePath(const char *path
,char **dirpart
,char **filepart
)
309 static char EOL
[] = "\0";
313 if ((fpart
= strrchr(path
, '/')) == NULL
) {
314 /* No slashes in path */
315 if ((dpart
= STRDUP(EOL
)) == NULL
)
317 if ((fpart
= STRDUP(path
)) == NULL
) {
323 if ((dpart
= STRDUP(path
)) == NULL
)
325 /* Include the slash -- Tuomo */
326 dpart
[fpart
- path
+ 1] = '\0';
327 if ((fpart
= STRDUP(++fpart
)) == NULL
) {
331 /* Root no longer a special case due above -- Tuomo
332 if (dpart[0] == '\0')
344 static int complete_homedir(const char *username
, char ***cp_ret
, char **beg
)
349 int n
=0, l
=strlen(username
);
353 for(pw
=getpwent(); pw
!=NULL
; pw
=getpwent()){
356 if(l
&& strncmp(name
, username
, l
))
359 name
=scat3("~", name
, "/");
366 cp
=REALLOC_N(*cp_ret
, char*, n
, n
+1);
384 name
[strlen(name
)-1]='\0';
386 if(pw
!=NULL
&& pw
->pw_dir
!=NULL
){
387 name
=scat(pw
->pw_dir
, "/");
399 * ret: 0 not a home directory,
400 * 1 home directory (repath set)
401 * 2 someone's home directory
403 static int tilde_complete(char *path
, char **retpath
)
412 if(*(path
+1)!='/' && *(path
+1)!='\0'){
433 *retpath
=scat3(home
, p
, "/");
435 *retpath
=scat(home
, p
);
438 return (*retpath
!=NULL
);
443 ** Return all possible completions.
445 int do_complete_file(char *pathname
, char ***avp
, char **beg
,
449 char *file
, *path
=NULL
, *tt
;
452 switch(tilde_complete(pathname
, &path
)){
454 i
=SplitPath(pathname
, &dir
, &file
);
457 return complete_homedir(pathname
+1, avp
, beg
);
459 i
=SplitPath(path
, &dir
, &file
);
465 ac
=FindMatches(dir
, file
, avp
);
469 if(ac
==0 && path
!=NULL
){
475 }else if(path
!=NULL
){
479 /* Identify directories with trailing / */
481 path
= NEW(char,strlen(dir
)+strlen((*avp
)[i
])+3);
482 sprintf(path
,"%s/%s",dir
,(*avp
)[i
]);
483 if(el_is_directory(path
)){
484 tt
= NEW(char,strlen((*avp
)[i
])+2);
485 sprintf(tt
,"%s/",(*avp
)[i
]);
499 ** Return all possible completions.
501 int do_complete_file_with_path(char *pathname
, char ***avp
, char **beg
,
505 char *file
, *path
=NULL
, *tt
;
506 int ac
=0, i
, dcomp
=FALSE
;
508 switch(tilde_complete(pathname
, &path
)){
510 i
=SplitRelativePath(pathname
, &dir
, &file
);
513 return complete_homedir(pathname
+1, avp
, beg
);
515 i
=SplitPath(path
, &dir
, &file
);
522 ac
=FindFullPathMatches(file
, avp
); /* No slashes in path so far. */
532 ac
=FindMatches(dir
, file
, avp
);
538 if(ac
==0 && path
!=NULL
){
544 }else if(path
!=NULL
){
548 /* Identify directories with trailing / */
550 for (i
= 0; i
< ac
; i
++) {
551 path
= NEW(char,strlen(dir
)+strlen((*avp
)[i
])+3);
552 sprintf(path
,"%s/%s",dir
,(*avp
)[i
]);
553 if (el_is_directory(path
)) {
554 tt
= NEW(char,strlen((*avp
)[i
])+2);
555 sprintf(tt
,"%s/",(*avp
)[i
]);
570 int main(int argc
, char *argv
[])
579 for(i
=1; i
<argc
; i
++){
580 if(strcmp(argv
[i
], "-h")==0){
581 printf("Usage: ion-completefile [-help] [-wp] [to_complete...]\n");
586 for(i
=1; i
<argc
; i
++){
587 if(strcmp(argv
[i
], "-wp")==0){
591 n
=do_complete_file_with_path(argv
[i
], &avp
, &beg
, NULL
);
593 n
=do_complete_file(argv
[i
], &avp
, &beg
, NULL
);
607 printf("%s\n", avp
[j
]);