cplfile.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
*
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* to whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
* OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
* INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Except as contained in this notice, the name of a copyright holder
* shall not be used in advertising or otherwise to promote the sale, use
* or other dealings in this Software without prior written authorization
* of the copyright holder.
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* If file-system access is to be excluded, this module has no function,
* so all of its code should be excluded.
*/
#ifndef WITHOUT_FILE_SYSTEM
/*
* Standard includes.
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
/*
* Local includes.
*/
#include "libtecla.h"
#include "direader.h"
#include "homedir.h"
#include "pathutil.h"
#include "cplfile.h"
#include "errmsg.h"
/*
* Set the maximum length allowed for usernames.
* names.
*/
#define USR_LEN 100
/*
* Set the maximum length allowed for environment variable names.
*/
#define ENV_LEN 100
/*
* The resources needed to complete a filename are maintained in objects
* of the following type.
*/
struct CompleteFile {
/* users. */
/* environment variables. */
};
static HOME_DIR_FN(cf_homedir_callback);
void *check_data);
int add_escapes);
/*
* A stack based object of the following type is used to pass data to the
* cf_homedir_callback() function.
*/
typedef struct {
const char *line; /* The line from which the prefix was extracted */
int word_start; /* The index in line[] of the start of the username */
int word_end; /* The index in line[] following the end of the prefix */
int escaped; /* If true, add escapes to the completion suffixes */
} CfHomeArgs;
/*.......................................................................
* Create a new file-completion object.
*
* Output:
* return CompleteFile * The new object, or NULL on error.
*/
CompleteFile *_new_CompleteFile(void)
{
/*
* Allocate the container.
*/
if(!cf) {
return NULL;
};
/*
* Before attempting any operation that might fail, initialize the
* container at least up to the point at which it can safely be passed
* to _del_CompleteFile().
*/
/*
* Allocate a place to record error messages.
*/
return _del_CompleteFile(cf);
/*
* Create the object that is used for reading directories.
*/
return _del_CompleteFile(cf);
/*
* Create the object that is used to lookup home directories.
*/
return _del_CompleteFile(cf);
/*
* Create the buffer in which the completed pathname is accumulated.
*/
return _del_CompleteFile(cf);
/*
* Create a pathname work buffer.
*/
return _del_CompleteFile(cf);
return cf;
}
/*.......................................................................
* Delete a file-completion object.
*
* Input:
* cf CompleteFile * The object to be deleted.
* Output:
* return CompleteFile * The deleted object (always NULL).
*/
{
if(cf) {
};
return NULL;
}
/*.......................................................................
* Look up the possible completions of the incomplete filename that
* lies between specified indexes of a given command-line string.
*
* Input:
* cpl WordCompletion * The object in which to record the completions.
* cf CompleteFile * The filename-completion resource object.
* line const char * The string containing the incomplete filename.
* word_start int The index of the first character in line[]
* of the incomplete filename.
* word_end int The index of the character in line[] that
* follows the last character of the incomplete
* filename.
* escaped int If true, backslashes in line[] are
* interpreted as escaping the characters
* that follow them, and any spaces, tabs,
* backslashes, or wildcard characters in the
* returned suffixes will be similarly escaped.
* If false, backslashes will be interpreted as
* literal parts of the file name, and no
* backslashes will be added to the returned
* suffixes.
* check_fn CplCheckFn * If not zero, this argument specifies a
* function to call to ask whether a given
* file should be included in the list
* of completions.
* check_data void * Anonymous data to be passed to check_fn().
* Output:
* return int 0 - OK.
* 1 - Error. A description of the error can be
* acquired by calling _cf_last_error(cf).
*/
{
const char *lptr; /* A pointer into line[] */
int nleft; /* The number of characters still to be processed */
/* in line[]. */
/*
* Check the arguments.
*/
if(cf) {
};
return 1;
};
/*
* Clear the buffer in which the filename will be constructed.
*/
/*
* How many characters are to be processed?
*/
/*
* Get a pointer to the start of the incomplete filename.
*/
/*
* If the first character is a tilde, then perform home-directory
* interpolation.
*/
int slen;
return 1;
/*
* Advance over the username in the input line.
*/
/*
* If we haven't hit the end of the input string then we have a complete
* username to translate to the corresponding home directory.
*/
if(nleft > 0) {
return 1;
/*
* ~user and ~ are usually followed by a directory separator to
* separate them from the file contained in the home directory.
* If the home directory is the root directory, then we don't want
* to follow the home directory by a directory separator, so we should
* skip over it so that it doesn't get copied into the filename.
*/
lptr += FS_DIR_SEP_LEN;
nleft -= FS_DIR_SEP_LEN;
};
/*
* If we have reached the end of the input string, then the username
* may be incomplete, and we should attempt to complete it.
*/
} else {
/*
* Look up the possible completions of the username.
*/
};
};
/*
* Copy the rest of the path, stopping to expand $envvar expressions
* where encountered.
*/
while(nleft > 0) {
int seglen; /* The length of the next segment to be copied */
/*
* Find the length of the next segment to be copied, stopping if an
* unescaped '$' is seen, or the end of the path is reached.
*/
if(escaped && c == '\\')
seglen++;
else if(c == '$')
break;
/*
* We will be completing the last component of the file name,
* so whenever a directory separator is seen, assume that it
* might be the start of the last component, and mark the character
* that follows it as the start of the name that is to be completed.
*/
if(nleft >= FS_DIR_SEP_LEN &&
};
};
/*
* We have reached either the end of the filename or the start of
* $environment_variable expression. Record the newly checked
* segment of the filename in the output filename, removing
* backslash-escapes where needed.
*/
return 1;
};
/*
* If the above loop finished before we hit the end of the filename,
* then this was because an unescaped $ was seen. In this case, interpolate
* the value of the environment variable that follows it into the output
* filename.
*/
if(nleft > 0) {
char *value; /* The value of the environment variable */
int vlen; /* The length of the value string */
int nlen; /* The length of the environment variable name */
/*
* Read the name of the environment variable.
*/
return 1;
/*
* Advance over the environment variable name in the input line.
*/
/*
* Get the value of the environment variable.
*/
if(!value) {
return 1;
};
/*
* If we are at the start of the filename and the first character of the
* environment variable value is a '~', attempt home-directory
* interpolation.
*/
return 1;
/*
* If the home directory is the root directory, and the ~usrname expression
* was followed by a directory separator, prevent the directory separator
* from being appended to the root directory by skipping it in the
* input line.
*/
lptr += FS_DIR_SEP_LEN;
nleft -= FS_DIR_SEP_LEN;
};
} else {
/*
* Append the value of the environment variable to the output path.
*/
return 1;
};
/*
* Prevent extra directory separators from being added.
*/
if(nleft >= FS_DIR_SEP_LEN &&
lptr += FS_DIR_SEP_LEN;
nleft -= FS_DIR_SEP_LEN;
} else if(vlen > FS_DIR_SEP_LEN &&
};
};
/*
* If adding the environment variable didn't form a valid directory,
* we can't complete the line, since there is no way to separate append
* a partial filename to an environment variable reference without
* that appended part of the name being seen later as part of the
* environment variable name. Thus if the currently constructed path
* isn't a directory, quite now with no completions having been
* registered.
*/
return 0;
/*
* For the reasons given above, if we have reached the end of the filename
* with the expansion of an environment variable, the only allowed
* completion involves the addition of a directory separator.
*/
if(nleft == 0) {
"", "")) {
return 1;
};
return 0;
};
};
};
/*
* Complete the filename if possible.
*/
}
/*.......................................................................
* Return a description of the last path-completion error that occurred.
*
* Input:
* cf CompleteFile * The path-completion resource object.
* Output:
* return const char * The description of the last error.
*/
{
}
/*.......................................................................
* Lookup the home directory of the specified user, or the current user
* if no name is specified, appending it to output pathname.
*
* Input:
* cf CompleteFile * The pathname completion resource object.
* user const char * The username to lookup, or "" to lookup the
* current user.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
{
/*
* Attempt to lookup the home directory.
*/
/*
* Failed?
*/
if(!home_dir) {
return 1;
};
/*
* Append the home directory to the pathname string.
*/
return 1;
};
return 0;
}
/*.......................................................................
* Lookup and report all completions of a given username prefix.
*
* Input:
* cf CompleteFile * The filename-completion resource object.
* cpl WordCompletion * The object in which to record the completions.
* prefix const char * The prefix of the usernames to lookup.
* line const char * The command-line in which the username appears.
* word_start int The index within line[] of the start of the
* username that is being completed.
* word_end int The index within line[] of the character which
* follows the incomplete username.
* escaped int True if the completions need to have special
* characters escaped.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
{
/*
* Set up a container of anonymous arguments to be sent to the
* username-lookup iterator.
*/
/*
* Iterate through the list of users, recording those which start
* with the specified prefix.
*/
return 1;
};
return 0;
}
/*.......................................................................
* The user/home-directory scanner callback function (see homedir.h)
* used by cf_complete_username().
*/
static HOME_DIR_FN(cf_homedir_callback)
{
/*
* Get the file-completion resources from the anonymous data argument.
*/
/*
* Copy the username into the pathname work buffer, adding backslash
* escapes where needed.
*/
return 1;
};
/*
* Report the completion suffix that was copied above.
*/
return 1;
};
return 0;
}
/*.......................................................................
* Report possible completions of the filename in cf->path->name[].
*
* Input:
* cf CompleteFile * The file-completion resource object.
* cpl WordCompletion * The object in which to record the completions.
* line const char * The input line, as received by the callback
* function.
* word_start int The index within line[] of the start of the
* last component of the filename that is being
* completed.
* word_end int The index within line[] of the character which
* follows the incomplete filename.
* escaped int If true, escape special characters in the
* completion suffixes.
* check_fn CplCheckFn * If not zero, this argument specifies a
* function to call to ask whether a given
* file should be included in the list
* of completions.
* check_data void * Anonymous data to be passed to check_fn().
* Output:
* return int 0 - OK.
* 1 - Error.
*/
void *check_data)
{
const char *dirpath; /* The name of the parent directory */
int start; /* The index of the start of the last filename */
/* component in the transcribed filename. */
const char *prefix; /* The filename prefix to be completed */
int prefix_len; /* The length of the filename prefix */
const char *file_name; /* The lastest filename being compared */
int waserr = 0; /* True after errors */
int terminated=0; /* True if the directory part had to be terminated */
/*
* Get the pathname string and its current length.
*/
/*
* Locate the start of the final component of the pathname.
*/
;
/*
* Is the parent directory the root directory?
*/
if(start==0 ||
start += FS_ROOT_DIR_LEN;
/*
* If we found a directory separator then the part which precedes the
* last component is the name of the directory to be opened.
*/
} else if(start > 0) {
/*
* The _dr_open_dir() function requires the directory name to be '\0'
* terminated, so temporarily do this by overwriting the first character
* of the directory separator.
*/
terminated = 1;
/*
* We reached the start of the pathname before finding a directory
* separator, so arrange to open the current working directory.
*/
} else {
start = 0;
};
/*
* Attempt to open the directory.
*/
return 1;
};
/*
* If removed above, restore the directory separator and skip over it
* to the start of the filename.
*/
if(terminated) {
start += FS_DIR_SEP_LEN;
};
/*
* Get the filename prefix and its length.
*/
/*
* Traverse the directory, looking for files who's prefixes match the
* last component of the pathname.
*/
/*
* Is the latest filename a possible completion of the filename prefix?
*/
/*
* When listing all files in a directory, don't list files that start
* with '.'. This is how hidden files are denoted in UNIX.
*/
/*
* Copy the completion suffix into the work pathname cf->buff->name,
* adding backslash escapes if needed.
*/
waserr = 1;
} else {
/*
* We want directories to be displayed with directory suffixes,
* and other fully completed filenames to be followed by spaces.
* To check the type of the file, append the current suffix
* to the path being completed, check the filetype, then restore
* the path to its original form.
*/
/* completed. */
"Insufficient memory to complete filename.",
return 1;
};
/*
* Specify suffixes according to the file type.
*/
cont_suffix = " ";
} else {
continue;
};
/*
* Remove the temporarily added suffix.
*/
/*
* Record the latest completion.
*/
waserr = 1;
};
};
};
};
/*
* Close the directory.
*/
return waserr;
}
/*.......................................................................
* Read a username or environment variable name, stopping when a directory
* separator is seen, when the end of the string is reached, or the
* output buffer overflows.
*
* Input:
* cf CompleteFile * The file-completion resource object.
* type char * The capitalized name of the type of name being read.
* string char * The string who's prefix contains the name.
* slen int The number of characters in string[].
* nambuf char * The output name buffer.
* nammax int The longest string that will fit in nambuf[], excluding
* the '\0' terminator.
* Output:
* return char * A pointer to nambuf on success. On error NULL is
* returned and a description of the error is recorded
* in cf->err.
*/
{
int namlen; /* The number of characters in nambuf[] */
const char *sptr; /* A pointer into string[] */
/*
* Work out the max number of characters that should be copied.
*/
/*
* Get the environment variable name that follows the dollar.
*/
namlen++) {
};
/*
* Did the name overflow the buffer?
*/
return NULL;
};
/*
* Terminate the string.
*/
return nambuf;
}
/*.......................................................................
* Using the work buffer cf->buff, make a suitably escaped copy of a
* given completion suffix, ready to be passed to cpl_add_completion().
*
* Input:
* cf CompleteFile * The file-completion resource object.
* suffix char * The suffix to be copied.
* add_escapes int If true, escape special characters.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
int add_escapes)
{
const char *sptr; /* A pointer into suffix[] */
int nbsl; /* The number of backslashes to add to the suffix */
int i;
/*
* How long is the suffix?
*/
/*
* Clear the work buffer.
*/
/*
* Count the number of backslashes that will have to be added to
* escape spaces, tabs, backslashes and wildcard characters.
*/
nbsl = 0;
if(add_escapes) {
switch(*sptr) {
case ' ': case '\t': case '\\': case '*': case '?': case '[':
nbsl++;
break;
};
};
};
/*
* Arrange for the output path buffer to have sufficient room for the
* both the suffix and any backslashes that have to be inserted.
*/
return 1;
};
/*
* If the suffix doesn't need any escapes, copy it directly into the
* work buffer.
*/
if(nbsl==0) {
} else {
/*
* Make a copy with special characters escaped?
*/
if(nbsl > 0) {
for(i=0; i<suffix_len; i++) {
switch(*src) {
case ' ': case '\t': case '\\': case '*': case '?': case '[':
*dst++ = '\\';
};
};
*dst = '\0';
};
};
return 0;
}
#endif /* ifndef WITHOUT_FILE_SYSTEM */