/* * p4prompt - Munge tcsh $prompt values to include client name * * Copyright (c) 2002 Anders Johnson <anders@ieee.org>. All rights * reserved. Refer to the accompanying README file for terms of use, * modification and distribution. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <limits.h> #include <ctype.h> #include <assert.h> #include <unistd.h> #ifdef P4API # include "apiGetClient.h" # include <sys/types.h> # include <sys/stat.h> #endif // P4API /* The character that separates filename components AND denotes root */ #define SEP '/' #define SEPSTR "/" /* Global */ char *me=NULL; void error() { perror(me); exit(3); } inline void *mymalloc(size_t size) { void *result=malloc(size); if(!result) { error(); } return result; } inline char *mystrcat(char *s1, char *s2) { char *result=mymalloc((strlen(s1)+strlen(s2)+1)*sizeof(char)); strcpy(result,s1); return strcat(result,s2); } inline char *strcatfree1(char *s1, char *s2) { char *result=mystrcat(s1, s2); free(s1); return result; } inline char *strcatfree2(char *s1, char *s2) { char *result=mystrcat(s1, s2); free(s2); return result; } inline char *mystrcat3(char *s1, char *s2, char *s3) { return strcatfree2(s1, mystrcat(s2, s3)); } inline char *mystrcpy(char *s) { return mystrcat(s, ""); } #define BUFSIZE 1024 char *myfgets(FILE *stream) { static char buf[BUFSIZE]; char *result; if(!fgets(buf, BUFSIZE, stream)) { return NULL; } result=mystrcpy(buf); while(buf[strlen(buf)-1] != '\n') { if(!fgets(buf, BUFSIZE, stream)) { return result; } result=strcatfree1(result, buf); } return result; } #define CLIENT "P4CLIENT" char *parse_config(FILE *fp) { char *line; char *result=NULL; int clen=strlen(CLIENT); /* Should be static, but isn't */ while((line=myfgets(fp)) != NULL) { char *cp=line; while(isspace(*cp)) { ++cp; } if(!strncmp(line, CLIENT, clen)) { cp+=clen; while(isspace(*cp)) { ++cp; } if(*cp == '=') { ++cp; while(isspace(*cp)) { ++cp; } result=mystrcpy(cp); cp=result; while(!isspace(*cp) && *cp!='#' && *cp) { ++cp; } *cp='\0'; break; } } free(line); } return result; } /* The following items are for finding the client w/o -p: */ char *p4config_value_; int setup_config() { p4config_value_=getenv("P4CONFIG"); if(!p4config_value_) { return 1; } return 0; } int is_root_config(char *dir, char **name) { char *cfgfile=mystrcat3(dir, SEPSTR, p4config_value_); FILE *fp; int found=0; if((fp=fopen(cfgfile,"r")) != NULL) { char *client=parse_config(fp); fclose(fp); if(client) { *name=client; } found=2; /* Stop looking */ } else if(errno!=ENOENT) { perror(me); found=1; /* Still need to look for root dir */ } free(cfgfile); return found; } #ifdef P4API /* The following items are for finding the client w/ -p: */ struct stat root_stat_; char *client_name_=NULL; int setup_p4api() { char *root; free(client_name_); /* Avoid memory leaks, in case of reuse */ apiGetClient(&client_name_, &root, me); if(client_name_ && root) { int result=0; if(stat(root, &root_stat_)) { perror(me); result=1; } free(root); return result; } else { return 1; } } int is_root_p4api(char *dir, char **name) { struct stat dir_stat; if(stat(dir, &dir_stat)) { perror(me); return 0; } if( dir_stat.st_dev == root_stat_.st_dev && dir_stat.st_ino == root_stat_.st_ino ) { *name=client_name_; return 2; } return 0; } #endif // P4API /* Returns the number of directory levels above the current directory * that represents the Perforce client root, or the filesystem root if * we don't appear to be in a client. If name is non-null, then *name * points to a string representing the name of the client. */ int find_client(char **name, int (*setup)(), int (*is_root)(char*, char**)) { int result=0; int found=0; char *pwd=getenv("PWD"); char *mypwd, *pwdp; assert(name); *name=NULL; if(!pwd) { fprintf(stderr, "%s: $PWD not set\n", me); return -1; } if(*pwd!=SEP) { fprintf(stderr, "%s: $PWD doesn't begin with " SEPSTR "\n", me ); return -1; } found=setup(); mypwd=mystrcpy(pwd); /* partial path */ pwdp=mypwd+strlen(mypwd); /* end of partial path */ while(*mypwd) { if(!found) { found=is_root(mypwd, name); if(found>1) { break; } } if(mypwd[1]) { ++result; --pwdp; if(*pwdp == SEP) { fprintf(stderr, "%s: $PWD has a misplaced " SEPSTR "\n", me ); result=-1; break; } } while(*pwdp != SEP) { --pwdp; } *pwdp='\0'; } free(mypwd); return result; } void print_clientcwd( char *client, int up, int value, int prepend, int ellipsis, int csh ) { char *pwd; char *prefix; char *pwdp; assert(client || csh); if(up > value && !prepend && !csh) { printf("%%c%d", value); return; } pwd=getenv("PWD"); if(!pwd) { /* We couldn't find $PWD, so just print "@" */ printf("@"); return; } prefix=mystrcpy(SEPSTR); if(client) { free(prefix); prefix=mystrcat("@", client); } else if(csh) { if(up<0) { /* We couldn't parse $PWD, so just print it as-is */ printf("%s", pwd); return; } else { char *home=getenv("HOME"); if(home && *home) { int len=strlen(home); if(strncmp(pwd, home, len)==0 && (pwd[len]==SEP || pwd[len]=='\0') ) { char *cp=home; free(prefix); prefix=mystrcpy("~"); while(cp[0] && cp[1]) { if(*cp==SEP) { assert(up); --up; } ++cp; } } } } } if(up <= value || prepend) { fputs(prefix, stdout); if(up && *prefix!=SEP) { putchar(SEP); } if(up > value) { if(ellipsis) { printf("..."); } else { printf("<%d>", up - value); } } } free(prefix); if(up > value) { up=value; } if(up) { pwdp=pwd+strlen(pwd); while(up) { assert(pwdp > pwd); --pwdp; if(*pwdp==SEP) { --up; } } fputs(pwdp+1, stdout); } return; } void print_prompt( char *prompt, char *client, int up, int ellipsis, int csh ) { char *cp=prompt; enum { idle, prefix, nest, nestprefix } state=idle; while(*cp) { switch(state) { case idle: if(*cp=='%') { state=prefix; } else { putchar(*cp); } break; case nest: putchar(*cp); if(*cp=='%') { state=nestprefix; } break; case nestprefix: putchar(*cp); if(*cp=='}') { state=idle; } break; case prefix: if(*cp=='@') { if(client || csh) { int prepend=0; int value=INT_MAX; unsigned sofar=0; ++cp; if(*cp=='0') { prepend=1; value=0; ++cp; } while(isdigit(*cp)) { sofar=10*sofar + (*cp - '0'); if(sofar>INT_MAX) { value=INT_MAX; } else { value=(int) sofar; } ++cp; } --cp; if(!value) { value=1; } print_clientcwd( client, up, value, prepend, ellipsis, csh ); } else { printf("%%c"); } state=idle; } else { putchar('%'); if(!csh || *cp!='%') { putchar(*cp); } if(*cp=='{' && !csh) { state=nest; } else { state=idle; } } break; default: abort(); /* Corrupt parser state */ } ++cp; } } void usage() { fprintf(stderr, "usage: %s [-c] [-e]" #ifdef P4API " [-p]" #endif // P4API " [--] <prompt-value>\n", me ); fprintf(stderr, " %s [-v]\n", me); exit(2); } int main(int argc, char **argv) { char *name=NULL; int ellipsis=0, csh=0; int up; int opt; int (*setup)()=setup_config; int (*is_root)(char*, char**)=is_root_config; if(argc) { me=argv[0]; } else { abort(); /* No command name? */ } while((opt=getopt(argc, argv, "ecpv"))!=-1) { switch(opt) { case 'c': csh=1; break; case 'e': ellipsis=1; break; case 'p': #ifdef P4API setup=setup_p4api; is_root=is_root_p4api; #else fprintf(stderr, "%s: -p option ignored\n", me); #endif // P4API break; case 'v': printf(NAME ", version " VERSION #ifdef P4API " with -p enabled" #endif // P4API "\n\n" ); printf( "Copyright (c) 2002 Anders Johnson <anders@ieee.org>. All rights\n" "reserved. Refer to the accompanying README file for terms of use,\n" "modification and distribution.\n" #ifdef P4API "Linked with the Perforce Client API, copyright 2001 Perforce Software, Inc." "\n" #endif // P4API ); exit(0); break; default: return 2; } } if(argc != optind+1) { usage(); } up=find_client(&name, setup, is_root); print_prompt(argv[optind], name, up, ellipsis, csh); printf("\n"); free(name); /* name might be NULL here, but that's OK */ return 0; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#2 | 1265 | anders_johnson |
p4prompt-0.02 release. Added -p option. |
||
#1 | 1211 | anders_johnson | First release of p4prompt. |