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)
76 #define EL_CONST const
79 #define COPYFROMTO(new, p, len) \
80 (void)memcpy((char *)(new), (char *)(p), (int)(len))
83 /* Hopefully one of these is defined... */
84 #define NGROUPS NGROUPS_MAX
87 typedef struct dirent DIRENTRY
;
89 static void el_add_slash(char *path
,char *p
)
93 if (stat(path
, &sb
) >= 0)
94 (void)strcat(p
, S_ISDIR(sb
.st_mode
) ? "/" : " ");
97 static int el_is_directory(char *path
)
101 if ((stat(path
, &sb
) >= 0) && S_ISDIR(sb
.st_mode
))
107 static int el_is_executable(char *path
)
109 unsigned int t_uid
= geteuid();
110 gid_t t_gids
[NGROUPS
];
115 groupcount
= getgroups(NGROUPS
, t_gids
);
117 if((stat(path
, &sb
) >= 0) && S_ISREG(sb
.st_mode
)) {
118 /* Normal file, see if we can execute it. */
120 if (sb
.st_mode
& S_IXOTH
) { /* All can execute */
123 if (sb
.st_uid
== t_uid
&& (sb
.st_mode
& S_IXUSR
)) {
126 if (sb
.st_mode
& S_IXGRP
) {
127 for (i
= 0; i
< groupcount
; i
++) {
128 if (sb
.st_gid
== t_gids
[i
]) {
139 ** strcmp-like sorting predicate for qsort.
141 /*STATIC int compare(EL_CONST void *p1,EL_CONST void *p2)
146 v1 = (EL_CONST char **)p1;
147 v2 = (EL_CONST char **)p2;
148 return strcmp(*v1, *v2);
152 ** Fill in *avp with an array of names that match file, up to its length.
155 static int FindMatches(char *dir
,char *file
,char ***avp
)
168 if ((dp
= opendir(dir
)) == NULL
)
174 while ((ep
= readdir(dp
)) != NULL
) {
176 if (p
[0] == '.' && (p
[1] == '\0' || (p
[1] == '.' && p
[2] == '\0')))
178 if (len
&& strncmp(p
, file
, len
) != 0)
181 if ((ac
% MEM_INC
) == 0) {
182 if ((new = NEW(char*, ac
+ MEM_INC
)) == NULL
)
185 COPYFROMTO(new, av
, ac
* sizeof (char **));
191 if ((av
[ac
] = STRDUP(p
)) == NULL
) {
199 /* Clean up and return. */
202 qsort(av, ac, sizeof (char **), compare);*/
208 ** Slight modification on FindMatches, to search through current PATH
211 static int FindFullPathMatches(char *file
,char ***avp
)
216 char *path
= getenv("PATH");
225 t_path
= strdup(path
);
231 t_t_path
= strtok(t_path
, ":\n\0");
233 if ((dp
= opendir(t_t_path
)) == NULL
) {
234 t_t_path
= strtok(NULL
, ":\n\0");
239 while ((ep
= readdir(dp
)) != NULL
) {
241 if (p
[0] == '.' && (p
[1] == '\0' || (p
[0] == '.' &&
246 if (len
&& strncmp(p
, file
, len
) != 0) {
250 snprintf(t_tmp
, 1024, "%s/%s", t_t_path
, p
);
251 if(!el_is_executable(t_tmp
)) {
255 if ((ac
% MEM_INC
) == 0) {
256 if ((new = NEW(char*, ac
+ MEM_INC
)) == NULL
) {
260 COPYFROMTO(new, av
, ac
* sizeof (char **));
265 if ((av
[ac
] = STRDUP(p
)) == NULL
) {
273 t_t_path
= strtok(NULL
, ":\n\0");
278 /* Clean up and return. */
281 qsort(av, ac, sizeof (char **), compare); */
289 ** Split a pathname into allocated directory and trailing filename parts.
291 STATIC
int SplitPath(const char *path
,char **dirpart
,char **filepart
)
293 static char DOT
[] = "./";
297 if ((fpart
= strrchr(path
, '/')) == NULL
) {
298 /* No slashes in path */
299 if ((dpart
= STRDUP(DOT
)) == NULL
)
301 if ((fpart
= STRDUP(path
)) == NULL
) {
306 if ((dpart
= STRDUP(path
)) == NULL
)
308 /* Include the slash -- Tuomo */
309 dpart
[fpart
- path
+ 1] = '\0';
310 if ((fpart
= STRDUP(++fpart
)) == NULL
) {
314 /* Root no longer a special case due above -- Tuomo
315 if (dpart[0] == '\0')
328 ** Split a pathname into allocated directory and trailing filename parts.
330 STATIC
int SplitRelativePath(const char *path
,char **dirpart
,char **filepart
)
332 static char DOT
[] = "./";
333 static char EOL
[] = "\0";
337 if ((fpart
= strrchr(path
, '/')) == NULL
) {
338 /* No slashes in path */
339 if ((dpart
= STRDUP(EOL
)) == NULL
)
341 if ((fpart
= STRDUP(path
)) == NULL
) {
347 if ((dpart
= STRDUP(path
)) == NULL
)
349 /* Include the slash -- Tuomo */
350 dpart
[fpart
- path
+ 1] = '\0';
351 if ((fpart
= STRDUP(++fpart
)) == NULL
) {
355 /* Root no longer a special case due above -- Tuomo
356 if (dpart[0] == '\0')
369 ** Attempt to complete the pathname, returning an allocated copy.
370 ** Fill in *unique if we completed it, or set it to 0 if ambiguous.
373 static char *el_complete(char *pathname
,int *unique
)
386 if (SplitPath(pathname
, &dir
, &file
) < 0)
389 if ((ac
= FindMatches(dir
, file
, &av
)) == 0) {
398 /* Exactly one match -- finish it off. */
400 j
= strlen(av
[0]) - len
+ 2;
401 if ((p
= NEW(char, j
+ 1)) != NULL
) {
402 COPYFROMTO(p
, av
[0] + len
, j
);
403 if ((new = NEW(char, strlen(dir
) + strlen(av
[0]) + 2)) != NULL
) {
404 (void)strcpy(new, dir
);
405 (void)strcat(new, "/");
406 (void)strcat(new, av
[0]);
407 el_add_slash(new, p
);
415 /* Find largest matching substring. */
416 for (i
= len
, end
= strlen(av
[0]); i
< end
; i
++)
417 for (j
= 1; j
< ac
; j
++)
418 if (av
[0][i
] != av
[j
][i
])
423 if ((p
= NEW(char, j
)) != NULL
) {
424 COPYFROMTO(p
, av
[0] + len
, j
);
431 /* Clean up and return. */
434 for (i
= 0; i
< ac
; i
++)
441 static int complete_homedir(const char *username
, char ***cp_ret
, char **beg
)
446 int n
=0, l
=strlen(username
);
450 for(pw
=getpwent(); pw
!=NULL
; pw
=getpwent()){
453 if(l
&& strncmp(name
, username
, l
))
456 name
=scat3("~", name
, "/");
463 cp
=REALLOC_N(*cp_ret
, char*, n
, n
+1);
481 name
[strlen(name
)-1]='\0';
483 if(pw
!=NULL
&& pw
->pw_dir
!=NULL
){
484 name
=scat(pw
->pw_dir
, "/");
496 * ret: 0 not a home directory,
497 * 1 home directory (repath set)
498 * 2 someone's home directory
500 static int tilde_complete(char *path
, char **retpath
)
509 if(*(path
+1)!='/' && *(path
+1)!='\0'){
530 *retpath
=scat3(home
, p
, "/");
532 *retpath
=scat(home
, p
);
535 return (*retpath
!=NULL
);
540 ** Return all possible completions.
542 int do_complete_file(char *pathname
, char ***avp
, char **beg
,
546 char *file
, *path
=NULL
, *tt
;
549 switch(tilde_complete(pathname
, &path
)){
551 i
=SplitPath(pathname
, &dir
, &file
);
554 return complete_homedir(pathname
+1, avp
, beg
);
556 i
=SplitPath(path
, &dir
, &file
);
562 ac
=FindMatches(dir
, file
, avp
);
566 if(ac
==0 && path
!=NULL
){
572 }else if(path
!=NULL
){
576 /* Identify directories with trailing / */
578 path
= NEW(char,strlen(dir
)+strlen((*avp
)[i
])+3);
579 sprintf(path
,"%s/%s",dir
,(*avp
)[i
]);
580 if(el_is_directory(path
)){
581 tt
= NEW(char,strlen((*avp
)[i
])+2);
582 sprintf(tt
,"%s/",(*avp
)[i
]);
596 ** Return all possible completions.
598 int do_complete_file_with_path(char *pathname
, char ***avp
, char **beg
,
602 char *file
, *path
=NULL
, *tt
;
603 int ac
=0, i
, dcomp
=FALSE
;
605 switch(tilde_complete(pathname
, &path
)){
607 i
=SplitRelativePath(pathname
, &dir
, &file
);
610 return complete_homedir(pathname
+1, avp
, beg
);
612 i
=SplitPath(path
, &dir
, &file
);
619 ac
=FindFullPathMatches(file
, avp
); /* No slashes in path so far. */
629 ac
=FindMatches(dir
, file
, avp
);
635 if(ac
==0 && path
!=NULL
){
641 }else if(path
!=NULL
){
645 /* Identify directories with trailing / */
647 for (i
= 0; i
< ac
; i
++) {
648 path
= NEW(char,strlen(dir
)+strlen((*avp
)[i
])+3);
649 sprintf(path
,"%s/%s",dir
,(*avp
)[i
]);
650 if (el_is_directory(path
)) {
651 tt
= NEW(char,strlen((*avp
)[i
])+2);
652 sprintf(tt
,"%s/",(*avp
)[i
]);
667 int main(int argc
, char *argv
[])
676 for(i
=1; i
<argc
; i
++){
677 if(strcmp(argv
[i
], "-h")==0){
678 printf("Usage: ion-completefile [-help] [-wp] [to_complete...]\n");
683 for(i
=1; i
<argc
; i
++){
684 if(strcmp(argv
[i
], "-wp")==0){
688 n
=do_complete_file_with_path(argv
[i
], &avp
, &beg
, NULL
);
690 n
=do_complete_file(argv
[i
], &avp
, &beg
, NULL
);
704 printf("%s\n", avp
[j
]);