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 #include <libtu/util.h>
65 #include <libtu/types.h>
66 #include <libtu/misc.h>
67 #include <libtu/output.h>
69 #define STRDUP(X) scopy(X)
70 #define DISPOSE(X) free(X)
71 #define NEW(T, X) ALLOC_N(T, X)
73 #define EL_CONST const
76 #define COPYFROMTO(new, p, len) \
77 (void)memcpy((char *)(new), (char *)(p), (int)(len))
80 /* Hopefully one of these is defined... */
81 #define NGROUPS NGROUPS_MAX
84 typedef struct dirent DIRENTRY;
86 static void el_add_slash(char *path,char *p)
90 if (stat(path, &sb) >= 0)
91 (void)strcat(p, S_ISDIR(sb.st_mode) ? "/" : " ");
94 static int el_is_directory(char *path)
98 if ((stat(path, &sb) >= 0) && S_ISDIR(sb.st_mode))
104 static int el_is_executable(char *path)
106 unsigned int t_uid = geteuid();
107 gid_t t_gids[NGROUPS];
112 groupcount = getgroups(NGROUPS, t_gids);
114 if((stat(path, &sb) >= 0) && S_ISREG(sb.st_mode)) {
115 /* Normal file, see if we can execute it. */
117 if (sb.st_mode & S_IXOTH) { /* All can execute */
120 if (sb.st_uid == t_uid && (sb.st_mode & S_IXUSR)) {
123 if (sb.st_mode & S_IXGRP) {
124 for (i = 0; i < groupcount; i++) {
125 if (sb.st_gid == t_gids[i]) {
136 ** strcmp-like sorting predicate for qsort.
138 /*STATIC int compare(EL_CONST void *p1,EL_CONST void *p2)
143 v1 = (EL_CONST char **)p1;
144 v2 = (EL_CONST char **)p2;
145 return strcmp(*v1, *v2);
149 ** Fill in *avp with an array of names that match file, up to its length.
152 static int FindMatches(char *dir,char *file,char ***avp)
165 if ((dp = opendir(dir)) == NULL)
171 while ((ep = readdir(dp)) != NULL) {
173 if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')))
175 if (len && strncmp(p, file, len) != 0)
178 if ((ac % MEM_INC) == 0) {
179 if ((new = NEW(char*, ac + MEM_INC)) == NULL)
182 COPYFROMTO(new, av, ac * sizeof (char **));
188 if ((av[ac] = STRDUP(p)) == NULL) {
196 /* Clean up and return. */
199 qsort(av, ac, sizeof (char **), compare);*/
205 ** Slight modification on FindMatches, to search through current PATH
208 static int FindFullPathMatches(char *file,char ***avp)
213 char *path = getenv("PATH");
222 t_path = strdup(path);
228 t_t_path = strtok(t_path, ":\n\0");
230 if ((dp = opendir(t_t_path)) == NULL) {
231 t_t_path = strtok(NULL, ":\n\0");
236 while ((ep = readdir(dp)) != NULL) {
238 if (p[0] == '.' && (p[1] == '\0' || (p[0] == '.' &&
243 if (len && strncmp(p, file, len) != 0) {
247 snprintf(t_tmp, 1024, "%s/%s", t_t_path, p);
248 if(!el_is_executable(t_tmp)) {
252 if ((ac % MEM_INC) == 0) {
253 if ((new = NEW(char*, ac + MEM_INC)) == NULL) {
257 COPYFROMTO(new, av, ac * sizeof (char **));
262 if ((av[ac] = STRDUP(p)) == NULL) {
270 t_t_path = strtok(NULL, ":\n\0");
275 /* Clean up and return. */
278 qsort(av, ac, sizeof (char **), compare); */
286 ** Split a pathname into allocated directory and trailing filename parts.
288 STATIC int SplitPath(const char *path,char **dirpart,char **filepart)
290 static char DOT[] = "./";
294 if ((fpart = strrchr(path, '/')) == NULL) {
295 /* No slashes in path */
296 if ((dpart = STRDUP(DOT)) == NULL)
298 if ((fpart = STRDUP(path)) == NULL) {
303 if ((dpart = STRDUP(path)) == NULL)
305 /* Include the slash -- Tuomo */
306 dpart[fpart - path + 1] = '\0';
307 if ((fpart = STRDUP(++fpart)) == NULL) {
311 /* Root no longer a special case due above -- Tuomo
312 if (dpart[0] == '\0')
325 ** Split a pathname into allocated directory and trailing filename parts.
327 STATIC int SplitRelativePath(const char *path,char **dirpart,char **filepart)
329 static char DOT[] = "./";
330 static char EOL[] = "\0";
334 if ((fpart = strrchr(path, '/')) == NULL) {
335 /* No slashes in path */
336 if ((dpart = STRDUP(EOL)) == NULL)
338 if ((fpart = STRDUP(path)) == NULL) {
344 if ((dpart = STRDUP(path)) == NULL)
346 /* Include the slash -- Tuomo */
347 dpart[fpart - path + 1] = '\0';
348 if ((fpart = STRDUP(++fpart)) == NULL) {
352 /* Root no longer a special case due above -- Tuomo
353 if (dpart[0] == '\0')
366 ** Attempt to complete the pathname, returning an allocated copy.
367 ** Fill in *unique if we completed it, or set it to 0 if ambiguous.
370 static char *el_complete(char *pathname,int *unique)
383 if (SplitPath(pathname, &dir, &file) < 0)
386 if ((ac = FindMatches(dir, file, &av)) == 0) {
395 /* Exactly one match -- finish it off. */
397 j = strlen(av[0]) - len + 2;
398 if ((p = NEW(char, j + 1)) != NULL) {
399 COPYFROMTO(p, av[0] + len, j);
400 if ((new = NEW(char, strlen(dir) + strlen(av[0]) + 2)) != NULL) {
401 (void)strcpy(new, dir);
402 (void)strcat(new, "/");
403 (void)strcat(new, av[0]);
404 el_add_slash(new, p);
412 /* Find largest matching substring. */
413 for (i = len, end = strlen(av[0]); i < end; i++)
414 for (j = 1; j < ac; j++)
415 if (av[0][i] != av[j][i])
420 if ((p = NEW(char, j)) != NULL) {
421 COPYFROMTO(p, av[0] + len, j);
428 /* Clean up and return. */
431 for (i = 0; i < ac; i++)
438 static int complete_homedir(const char *username, char ***cp_ret, char **beg)
443 int n=0, l=strlen(username);
447 for(pw=getpwent(); pw!=NULL; pw=getpwent()){
450 if(l && strncmp(name, username, l))
453 name=scat3("~", name, "/");
460 cp=REALLOC_N(*cp_ret, char*, n, n+1);
478 name[strlen(name)-1]='\0';
480 if(pw!=NULL && pw->pw_dir!=NULL){
481 name=scat(pw->pw_dir, "/");
493 * ret: 0 not a home directory,
494 * 1 home directory (repath set)
495 * 2 someone's home directory
497 static int tilde_complete(char *path, char **retpath)
506 if(*(path+1)!='/' && *(path+1)!='\0'){
527 *retpath=scat3(home, p, "/");
529 *retpath=scat(home, p);
532 return (*retpath!=NULL);
537 ** Return all possible completions.
539 int do_complete_file(char *pathname, char ***avp, char **beg,
543 char *file, *path=NULL, *tt;
546 switch(tilde_complete(pathname, &path)){
548 i=SplitPath(pathname, &dir, &file);
551 return complete_homedir(pathname+1, avp, beg);
553 i=SplitPath(path, &dir, &file);
559 ac=FindMatches(dir, file, avp);
563 if(ac==0 && path!=NULL){
569 }else if(path!=NULL){
573 /* Identify directories with trailing / */
575 path = NEW(char,strlen(dir)+strlen((*avp)[i])+3);
576 sprintf(path,"%s/%s",dir,(*avp)[i]);
577 if(el_is_directory(path)){
578 tt = NEW(char,strlen((*avp)[i])+2);
579 sprintf(tt,"%s/",(*avp)[i]);
593 ** Return all possible completions.
595 int do_complete_file_with_path(char *pathname, char ***avp, char **beg,
599 char *file, *path=NULL, *tt;
600 int ac=0, i, dcomp=FALSE;
602 switch(tilde_complete(pathname, &path)){
604 i=SplitRelativePath(pathname, &dir, &file);
607 return complete_homedir(pathname+1, avp, beg);
609 i=SplitPath(path, &dir, &file);
616 ac=FindFullPathMatches(file, avp); /* No slashes in path so far. */
626 ac=FindMatches(dir, file, avp);
632 if(ac==0 && path!=NULL){
638 }else if(path!=NULL){
642 /* Identify directories with trailing / */
644 for (i = 0; i < ac; i++) {
645 path = NEW(char,strlen(dir)+strlen((*avp)[i])+3);
646 sprintf(path,"%s/%s",dir,(*avp)[i]);
647 if (el_is_directory(path)) {
648 tt = NEW(char,strlen((*avp)[i])+2);
649 sprintf(tt,"%s/",(*avp)[i]);
664 int main(int argc, char *argv[])
673 for(i=1; i<argc; i++){
674 if(strcmp(argv[i], "-h")==0){
675 printf("Usage: ion-completefile [-help] [-wp] [to_complete...]\n");
680 for(i=1; i<argc; i++){
681 if(strcmp(argv[i], "-wp")==0){
685 n=do_complete_file_with_path(argv[i], &avp, &beg, NULL);
687 n=do_complete_file(argv[i], &avp, &beg, NULL);
701 printf("%s\n", avp[j]);