/* */ /* * aquery.c : Programmatic Prospero interface to Archie */ /* * */ /* * Copyright (c) 1991 by the University of Washington */ /* * */ /* * For copying and distribution information, please see the file */ /* * . */ #include "pmachine.h" #include "pfs.h" #include "perrno.h" #include "archie.h" static void translateArchieResponse(); inline static int hostnamecmp(); extern int pwarn; extern char p_warn_string[]; /* */ /* * archie_query : Sends a request to _host_, telling it to search for */ /* * _string_ using _query_ as the search method. */ /* * No more than _max_hits_ matches are to be returned */ /* * skipping over _offset_ matches. */ /* * */ /* * archie_query returns a linked list of virtual links. */ /* * If _flags_ does not include AQ_NOTRANS, then the Archie */ /* * responses will be translated. If _flags_ does not include */ /* * AQ_NOSORT, then the list will be sorted using _cmp_proc_ to */ /* * compare pairs of links. If _cmp_proc_ is NULL or AQ_DEFCMP, */ /* * then the default comparison procedure, defcmplink(), is used */ /* * sorting by host, then filename. If cmp_proc is AQ_INVDATECMP */ /* * then invdatecmplink() is used, sorting inverted by date. */ /* * otherwise a user-defined comparison procedure is called. */ /* * */ /* * archie_query returns NULL and sets perrno if the query */ /* * failed. Note that it can return NULL with perrno == PSUCCESS */ /* * if the query didn't fail but there were simply no matches. */ /* * */ /* * query: S Substring search ignoring case */ /* * C Substring search with case significant */ /* * R Regular expression search */ /* * = Exact String Match */ /* * s,c,e Tries exact match first and falls back to S, C, or R */ /* * if not found. */ /* * */ /* * cmp_proc: AQ_DEFCMP Sort by host, then filename */ /* * AQ_INVDATECMP Sort inverted by date */ /* * */ /* * flags: AQ_NOSORT Don't sort results */ /* * AQ_NOTRANS Don't translate results */ VLINK archie_query(host,string,max_hits,offset,query,cmp_proc,flags) char *host,*string; int max_hits,offset; Query query; int (*cmp_proc)(); int flags; { char qstring[MAX_VPATH]; /* For construting the query */ VLINK links; /* Matches returned by server */ VDIR_ST dir_st; /* Filled in by get_vdir */ PVDIR dir= &dir_st; VLINK p,q,r,lowest,nextp,pnext,pprev; int tmp; /* Set the cmp_proc if not given */ if (cmp_proc == NULL) cmp_proc = defcmplink; /* Make the query string */ sprintf(qstring,"ARCHIE/MATCH(%d,%d,%c)/%s", max_hits,offset, (char) query,string); /* Initialize Prospero structures */ perrno = PSUCCESS; *p_err_string = '\0'; pwarn = PNOWARN; *p_warn_string = '\0'; vdir_init(dir); /* Retrieve the list of matches, return error if there was one */ if((tmp = get_vdir(host,qstring,"",dir,GVD_ATTRIB|GVD_NOSORT,NULL,NULL))) { perrno = tmp; return(NULL); } /* Save the links, and clear in dir in case it's used again */ links = dir->links; dir->links = NULL; /* As returned, list is sorted by suffix, and conflicting suffixes */ /* appear on a list of "replicas". We want to create a */ /* one-dimensional list sorted by host then filename and maybe by */ /* some other parameter */ /* First flatten the doubly-linked list */ for (p = links; p != NULL; p = nextp) { nextp = p->next; if (p->replicas != NULL) { p->next = p->replicas; p->next->previous = p; for (r = p->replicas; r->next != NULL; r = r->next) /*EMPTY*/ ; r->next = nextp; nextp->previous = r; p->replicas = NULL; } } /* Translate the filenames unless NOTRANS was given */ if (!(flags & AQ_NOTRANS)) for (p = links; p != NULL; p = p->next) translateArchieResponse(p); /* If NOSORT given, then just hand it back */ if (flags & AQ_NOSORT) { perrno = PSUCCESS; return(links); } /* Otherwise sort it using a selection sort and the given cmp_proc */ for (p = links; p != NULL; p = nextp) { nextp = p->next; lowest = p; for (q = p->next; q != NULL; q = q->next) if ((*cmp_proc)(q,lowest) < 0) lowest = q; if (p != lowest) { /* swap the two links */ pnext = p->next; pprev = p->previous; if (lowest->next != NULL) lowest->next->previous = p; p->next = lowest->next; if (nextp == lowest) { p->previous = lowest; } else { lowest->previous->next = p; p->previous = lowest->previous; } if (nextp == lowest) { lowest->next = p; } else { pnext->previous = lowest; lowest->next = pnext; } if (pprev != NULL) pprev->next = lowest; lowest->previous = pprev; /* keep the head of the list in the right place */ if (links == p) links = lowest; } } /* Return the links */ perrno = PSUCCESS; return(links); } /* */ /* * translateArchieResponse: */ /* * */ /* * If the given link is for an archie-pseudo directory, fix it. */ /* * This is called unless AQ_NOTRANS was given to archie_query(). */ static void translateArchieResponse(l) VLINK l; { char *slash; if (strcmp(l->type,"DIRECTORY") == 0) { if (strncmp(l->filename,"ARCHIE/HOST",11) == 0) { l->type = stcopyr("EXTERNAL(AFTP,DIRECTORY)",l->type); l->host = stcopyr(l->filename+12,l->host); slash = (char *)index(l->host,'/'); if (slash) { l->filename = stcopyr(slash,l->filename); *slash++ = '\0'; } else l->filename = stcopyr("",l->filename); } } } /* hostnamecmp: Compare two hostnames based on domain, */ /* * right-to-left. Returns <0 if a belongs before b, >0 */ /* * if b belongs before a, and 0 if they are identical. */ /* * Contributed by asami@cs.berkeley.edu (Satoshi ASAMI). */ inline static int hostnamecmp(a,b) char *a,*b; { char *pa, *pb; int result; for (pa = a ; *pa ; pa++) ; for (pb = b ; *pb ; pb++) ; while (pa > a && pb > b) { for (; pa > a ; pa--) if (*pa == '.') { pa++; break; } for (; pb > b ; pb--) if (*pb == '.') { pb++; break; } if ((result = strcmp(pa, pb))) return (result); pa -= 2; pb -= 2; } if (pa <= a) { if (pb <= b) return (0); else return (-1); } else return (1); } /* */ /* * defcmplink: The default link comparison function for sorting. Compares */ /* * links p and q first by host then by filename. Returns < 0 if p */ /* * belongs before q, > 0 if p belongs after q, and == 0 if their */ /* * host and filename fields are identical. */ int defcmplink(p,q) VLINK p,q; { int result; if ((result=hostnamecmp(p->host,q->host)) != 0) return(result); else return(strcmp(p->filename,q->filename)); } /* */ /* * invdatecmplink: An alternative comparison function for sorting that */ /* * compares links p and q first by LAST-MODIFIED date, */ /* * if they both have that attribute. If both links */ /* * don't have that attribute or the dates are the */ /* * same, it then calls defcmplink() and returns its */ /* * value. */ int invdatecmplink(p,q) VLINK p,q; { PATTRIB pat,qat; char *pdate,*qdate; int result; pdate = qdate = NULL; for (pat = p->lattrib; pat; pat = pat->next) if(strcmp(pat->aname,"LAST-MODIFIED") == 0) pdate = pat->value.ascii; for (qat = q->lattrib; qat; qat = qat->next) if(strcmp(qat->aname,"LAST-MODIFIED") == 0) qdate = qat->value.ascii; if(!pdate && !qdate) return(defcmplink(p,q)); if(!pdate) return(1); if(!qdate) return(-1); if((result=strcmp(qdate,pdate)) == 0) return(defcmplink(p,q)); else return(result); }