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>
65 #include <libtu/util.h>
66 #include <libtu/types.h>
67 #include <libtu/misc.h>
68 #include <libtu/output.h>
70 #define STRDUP(X) scopy(X)
71 #define DISPOSE(X) free(X)
72 #define NEW(T, X) ALLOC_N(T, X)
74 #define EL_CONST const
77 #define COPYFROMTO(new, p, len) \
78 (void)memcpy((char *)(new), (char *)(p), (int)(len))
81 /* Hopefully one of these is defined... */
82 #define NGROUPS NGROUPS_MAX
85 typedef struct dirent DIRENTRY;
87 static void el_add_slash(char *path,char *p)
91 if (stat(path, &sb) >= 0)
92 (void)strcat(p, S_ISDIR(sb.st_mode) ? "/" : " ");
95 static int el_is_directory(char *path)
99 if ((stat(path, &sb) >= 0) && S_ISDIR(sb.st_mode))
105 static int el_is_executable(char *path)
107 unsigned int t_uid = geteuid();
108 gid_t t_gids[NGROUPS];
113 groupcount = getgroups(NGROUPS, t_gids);
115 if((stat(path, &sb) >= 0) && S_ISREG(sb.st_mode)) {
116 /* Normal file, see if we can execute it. */
118 if (sb.st_mode & S_IXOTH) { /* All can execute */
121 if (sb.st_uid == t_uid && (sb.st_mode & S_IXUSR)) {
124 if (sb.st_mode & S_IXGRP) {
125 for (i = 0; i < groupcount; i++) {
126 if (sb.st_gid == t_gids[i]) {
137 ** strcmp-like sorting predicate for qsort.
139 /*STATIC int compare(EL_CONST void *p1,EL_CONST void *p2)
144 v1 = (EL_CONST char **)p1;
145 v2 = (EL_CONST char **)p2;
146 return strcmp(*v1, *v2);
150 ** Fill in *avp with an array of names that match file, up to its length.
153 static int FindMatches(char *dir,char *file,char ***avp)
166 if ((dp = opendir(dir)) == NULL)
172 while ((ep = readdir(dp)) != NULL) {
174 if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')))
176 if (len && strncmp(p, file, len) != 0)
179 if ((ac % MEM_INC) == 0) {
180 if ((new = NEW(char*, ac + MEM_INC)) == NULL)
183 COPYFROMTO(new, av, ac * sizeof (char **));
189 if ((av[ac] = STRDUP(p)) == NULL) {
197 /* Clean up and return. */
200 qsort(av, ac, sizeof (char **), compare);*/
206 ** Slight modification on FindMatches, to search through current PATH
209 static int FindFullPathMatches(char *file,char ***avp)
214 char *path = getenv("PATH");
223 t_path = strdup(path);
229 t_t_path = strtok(t_path, ":\n\0");
231 if ((dp = opendir(t_t_path)) == NULL) {
232 t_t_path = strtok(NULL, ":\n\0");
237 while ((ep = readdir(dp)) != NULL) {
239 if (p[0] == '.' && (p[1] == '\0' || (p[0] == '.' &&
244 if (len && strncmp(p, file, len) != 0) {
248 snprintf(t_tmp, 1024, "%s/%s", t_t_path, p);
249 if(!el_is_executable(t_tmp)) {
253 if ((ac % MEM_INC) == 0) {
254 if ((new = NEW(char*, ac + MEM_INC)) == NULL) {
258 COPYFROMTO(new, av, ac * sizeof (char **));
263 if ((av[ac] = STRDUP(p)) == NULL) {
271 t_t_path = strtok(NULL, ":\n\0");
276 /* Clean up and return. */
279 qsort(av, ac, sizeof (char **), compare); */
287 ** Split a pathname into allocated directory and trailing filename parts.
289 STATIC int SplitPath(const char *path,char **dirpart,char **filepart)
291 static char DOT[] = "./";
295 if ((fpart = strrchr(path, '/')) == NULL) {
296 /* No slashes in path */
297 if ((dpart = STRDUP(DOT)) == NULL)
299 if ((fpart = STRDUP(path)) == NULL) {
304 if ((dpart = STRDUP(path)) == NULL)
306 /* Include the slash -- Tuomo */
307 dpart[fpart - path + 1] = '\0';
308 if ((fpart = STRDUP(++fpart)) == NULL) {
312 /* Root no longer a special case due above -- Tuomo
313 if (dpart[0] == '\0')
326 ** Split a pathname into allocated directory and trailing filename parts.
328 STATIC int SplitRelativePath(const char *path,char **dirpart,char **filepart)
330 static char DOT[] = "./";
331 static char EOL[] = "\0";
335 if ((fpart = strrchr(path, '/')) == NULL) {
336 /* No slashes in path */
337 if ((dpart = STRDUP(EOL)) == NULL)
339 if ((fpart = STRDUP(path)) == NULL) {
345 if ((dpart = STRDUP(path)) == NULL)
347 /* Include the slash -- Tuomo */
348 dpart[fpart - path + 1] = '\0';
349 if ((fpart = STRDUP(++fpart)) == NULL) {
353 /* Root no longer a special case due above -- Tuomo
354 if (dpart[0] == '\0')
367 ** Attempt to complete the pathname, returning an allocated copy.
368 ** Fill in *unique if we completed it, or set it to 0 if ambiguous.
371 static char *el_complete(char *pathname,int *unique)
384 if (SplitPath(pathname, &dir, &file) < 0)
387 if ((ac = FindMatches(dir, file, &av)) == 0) {
396 /* Exactly one match -- finish it off. */
398 j = strlen(av[0]) - len + 2;
399 if ((p = NEW(char, j + 1)) != NULL) {
400 COPYFROMTO(p, av[0] + len, j);
401 if ((new = NEW(char, strlen(dir) + strlen(av[0]) + 2)) != NULL) {
402 (void)strcpy(new, dir);
403 (void)strcat(new, "/");
404 (void)strcat(new, av[0]);
405 el_add_slash(new, p);
413 /* Find largest matching substring. */
414 for (i = len, end = strlen(av[0]); i < end; i++)
415 for (j = 1; j < ac; j++)
416 if (av[0][i] != av[j][i])
421 if ((p = NEW(char, j)) != NULL) {
422 COPYFROMTO(p, av[0] + len, j);
429 /* Clean up and return. */
432 for (i = 0; i < ac; i++)
439 static int complete_homedir(const char *username, char ***cp_ret, char **beg)
444 int n=0, l=strlen(username);
448 for(pw=getpwent(); pw!=NULL; pw=getpwent()){
451 if(l && strncmp(name, username, l))
454 name=scat3("~", name, "/");
461 cp=REALLOC_N(*cp_ret, char*, n, n+1);
479 name[strlen(name)-1]='\0';
481 if(pw!=NULL && pw->pw_dir!=NULL){
482 name=scat(pw->pw_dir, "/");
494 * ret: 0 not a home directory,
495 * 1 home directory (repath set)
496 * 2 someone's home directory
498 static int tilde_complete(char *path, char **retpath)
507 if(*(path+1)!='/' && *(path+1)!='\0'){
528 *retpath=scat3(home, p, "/");
530 *retpath=scat(home, p);
533 return (*retpath!=NULL);
538 ** Return all possible completions.
540 int do_complete_file(char *pathname, char ***avp, char **beg,
544 char *file, *path=NULL, *tt;
547 switch(tilde_complete(pathname, &path)){
549 i=SplitPath(pathname, &dir, &file);
552 return complete_homedir(pathname+1, avp, beg);
554 i=SplitPath(path, &dir, &file);
560 ac=FindMatches(dir, file, avp);
564 if(ac==0 && path!=NULL){
570 }else if(path!=NULL){
574 /* Identify directories with trailing / */
576 path = NEW(char,strlen(dir)+strlen((*avp)[i])+3);
577 sprintf(path,"%s/%s",dir,(*avp)[i]);
578 if(el_is_directory(path)){
579 tt = NEW(char,strlen((*avp)[i])+2);
580 sprintf(tt,"%s/",(*avp)[i]);
594 ** Return all possible completions.
596 int do_complete_file_with_path(char *pathname, char ***avp, char **beg,
600 char *file, *path=NULL, *tt;
601 int ac=0, i, dcomp=FALSE;
603 switch(tilde_complete(pathname, &path)){
605 i=SplitRelativePath(pathname, &dir, &file);
608 return complete_homedir(pathname+1, avp, beg);
610 i=SplitPath(path, &dir, &file);
617 ac=FindFullPathMatches(file, avp); /* No slashes in path so far. */
627 ac=FindMatches(dir, file, avp);
633 if(ac==0 && path!=NULL){
639 }else if(path!=NULL){
643 /* Identify directories with trailing / */
645 for (i = 0; i < ac; i++) {
646 path = NEW(char,strlen(dir)+strlen((*avp)[i])+3);
647 sprintf(path,"%s/%s",dir,(*avp)[i]);
648 if (el_is_directory(path)) {
649 tt = NEW(char,strlen((*avp)[i])+2);
650 sprintf(tt,"%s/",(*avp)[i]);
665 int main(int argc, char *argv[])
674 for(i=1; i<argc; i++){
675 if(strcmp(argv[i], "-h")==0){
676 printf("Usage: ion-completefile [-help] [-wp] [to_complete...]\n");
681 for(i=1; i<argc; i++){
682 if(strcmp(argv[i], "-wp")==0){
686 n=do_complete_file_with_path(argv[i], &avp, &beg, NULL);
688 n=do_complete_file(argv[i], &avp, &beg, NULL);
702 printf("%s\n", avp[j]);