1: static char *rcsid = "$Header$"; 2: /* 3: * rmproject - remove a project root directory 4: * 5: * Author: Peter J. Nicklin 6: */ 7: #include <sys/param.h> 8: #include <signal.h> 9: #include <stdio.h> 10: #include "getarg.h" 11: #include "macro.h" 12: #include "null.h" 13: #include "path.h" 14: #include "pdb.h" 15: #include "pld.h" 16: #include "slist.h" 17: #include "spms.h" 18: #include "system.h" 19: #include "yesno.h" 20: 21: char CWD[PATHSIZE]; /* current working directory */ 22: char *CWP; /* current working project */ 23: char *PGN = "rmproject"; /* program name */ 24: char *RMFLAG = ""; /* rm "-f" flag */ 25: int FORCE = 0; /* brute force remove or undefine */ 26: int RECURSIVE = 0; /* remove project dirs recursively */ 27: int UNDEFINE = 0; /* remove proj dir definitions only */ 28: int WANT_TO_EXIT = 0; /* advisory exit flag */ 29: 30: main(argc, argv) 31: int argc; 32: char **argv; 33: { 34: extern int PPDEBUG; /* project pathname debug flag */ 35: char *getcwp(); /* get current working project */ 36: char *getwd(); /* get current working directory */ 37: int isfg(); /* is process in foreground? */ 38: int onintr(); /* process signals */ 39: int rmproject(); /* remove a project root directory */ 40: int rmtyp(); /* remove project dir type labels */ 41: int status = 0; /* exit status */ 42: int unproject(); /* undefine project root directory */ 43: int xppath(); /* expand project pathname */ 44: PATH pathbuf; /* pathname struct buffer */ 45: SLIST *pdirtyp; /* project directory type labels list */ 46: SLIST *slinit(); /* initialize singly-linked list */ 47: void typargtolist(); /* type labels -> pdirtyp list */ 48: 49: pdirtyp = slinit(); 50: { 51: register char *s; /* option pointer */ 52: while (--argc > 0 && **++argv == '-') 53: { 54: for (s = argv[0]+1; *s != '\0'; s++) 55: switch (*s) 56: { 57: case 'D': 58: PPDEBUG = YES; 59: break; 60: case 'F': 61: /* 62: * 'F' option is mentioned in 63: * rmproject() and unproject() 64: */ 65: FORCE++; 66: break; 67: case 'T': 68: typargtolist(GETARG(s), pdirtyp); 69: if (*s == '\0') 70: status = 1; 71: goto endfor; 72: case 'f': 73: RMFLAG = "-f"; 74: break; 75: case 'r': 76: RECURSIVE++; 77: break; 78: case 'u': 79: UNDEFINE++; 80: break; 81: default: 82: warn("bad option -%c", *s); 83: status = 1; 84: goto endfor; 85: } 86: endfor: continue; 87: } 88: } 89: if (status == 1 || argc < 1) 90: fatal("usage: rmproject [-fru] [-T type[,type...]] projectname ..."); 91: 92: if ((CWP = getcwp()) == NULL) 93: fatal("no project environment"); 94: if (getwd(CWD) == NULL) 95: fatal("can't find current working directory"); 96: if (isfg() == YES) 97: { 98: signal(SIGINT, onintr); 99: signal(SIGQUIT, onintr); 100: signal(SIGHUP, onintr); 101: } 102: 103: for (; argc > 0; ++argv, --argc) 104: { 105: if (xppath(*argv, &pathbuf) == -1) 106: { 107: patherr(*argv); 108: status = 1; 109: continue; 110: } 111: switch (pathbuf.p_mode & P_IFMT) 112: { 113: case P_IFHOME: 114: case P_IFPROOT: 115: if (SLNUM(pdirtyp) > 0) 116: status |= rmtyp(*argv, pdirtyp, &pathbuf); 117: else if (UNDEFINE) 118: status |= unproject(*argv, &pathbuf); 119: else 120: status |= rmproject(*argv, &pathbuf); 121: break; 122: case P_IFPDIR: 123: warn("%s is a project directory", *argv); 124: status = 1; 125: break; 126: case P_IFNEW: 127: case P_IFREG: 128: warn("%s: no such project", *argv); 129: status = 1; 130: break; 131: } 132: if (WANT_TO_EXIT) 133: exit(1); 134: } 135: exit(status); 136: } 137: 138: 139: 140: /* 141: * onintr() resets interrupt, quit, and hangup signals, and sets a flag 142: * which advises the process to exit at the first opportunity. 143: */ 144: onintr() 145: { 146: signal(SIGINT, onintr); 147: signal(SIGQUIT, onintr); 148: signal(SIGHUP, onintr); 149: 150: WANT_TO_EXIT = 1; 151: } 152: 153: 154: 155: /* 156: * pbrmtyp() removes type labels from database buffer. 157: */ 158: void 159: pbrmtyp(ppathname, typlist) 160: char *ppathname; /* project pathname */ 161: SLIST *typlist; /* type labels list */ 162: { 163: char *pbgetstring(); /* get specified string field */ 164: char *pdtfind(); /* find type label in buffer */ 165: char *slget(); /* get next key from list */ 166: char *tp; /* pointer to type label */ 167: char typbuf[TYPBUFSIZE]; /* project directory types buffer */ 168: int pbaddstring(); /* add string field */ 169: int strlen(); /* string length */ 170: void slrewind(); /* rewind list */ 171: void pdtrm(); /* remove type label */ 172: 173: pbgetstring(PDIRTYPE, typbuf); 174: slrewind(typlist); 175: while ((tp = slget(typlist)) != NULL) 176: { 177: if (pdtfind(tp, typbuf) != NULL) 178: pdtrm(tp, typbuf); 179: else 180: warn("%s: \"%s\" type label not found", ppathname, tp); 181: } 182: pbaddstring(PDIRTYPE, typbuf); 183: } 184: 185: 186: 187: /* 188: * rmd() removes a project directory. rmd() returns the status of the rm 189: * command or 1 if the user decides not to remove a project directory. 190: * Before removing a directory the project link directory is moved to 191: * safe place. If the directory is removed successfully, the project link 192: * directory is removed. 193: */ 194: rmd(pathname) 195: char *pathname; /* full pathname of directory */ 196: { 197: char cmdbuf[PATHSIZE+9]; /* command buffer */ 198: int rmpld(); /* remove project link directory */ 199: int savepld(); /* save project link directory */ 200: int status; /* return status */ 201: int yes(); /* is reply yes? */ 202: void restorpld(); /* restore project link directory */ 203: void unsavepld(); /* remove saved project link dir */ 204: 205: if (RECURSIVE) 206: { 207: sprintf(cmdbuf, "rm %s -r %s", RMFLAG, pathname); 208: printf("%s? [yn](n): ", cmdbuf); 209: if (!yes()) 210: return(1); 211: status = system(cmdbuf); 212: status >>= NBBY; 213: status &= 0xff; 214: } 215: else { 216: if ((status = savepld(pathname)) == 0) 217: if ((status = rmpld(pathname)) == 0) 218: if ((status = RM_DIR(pathname)) != 0) 219: restorpld(pathname); 220: else 221: unsavepld(); 222: } 223: return(status); 224: } 225: 226: 227: 228: /* 229: * rmpld() removes a project link directory. Returns 1 if file not 230: * removed, otherwise zero. 231: */ 232: rmpld(pathname) 233: char *pathname; /* project root directory pathname */ 234: { 235: char *pathcat(); /* pathname concatenation */ 236: char pldpathname[PATHSIZE]; /* project link directory pathname */ 237: 238: if (unlink(pathcat(pldpathname, pathname, PLDNAME)) != 0) 239: if (!FORCE) 240: { 241: pperror(pldpathname); 242: return(1); 243: } 244: return(0); 245: } 246: 247: 248: 249: /* 250: * rmproject() removes a project root directory. Returns 0 is successful, 251: * otherwise 1. 252: */ 253: rmproject(ppathname, pb) 254: char *ppathname; /* project root directory pathname */ 255: PATH *pb; /* pathname struct buffer */ 256: { 257: char *ppathcat(); /* project pathname concatenation */ 258: char *ppathhead(); /* remove tail of project pathname */ 259: char pppathname[PPATHSIZE]; /* parent project pathname */ 260: char *strcpy(); /* string copy */ 261: int _closepdb(); /* close database without updating */ 262: int closepdb(); /* close database */ 263: int errpdb(); /* print database error */ 264: int plen; /* length of regular pathname */ 265: int pputent(); /* write buffer to database */ 266: int pstatus = 0; /* project status */ 267: int strlen(); /* string length */ 268: int strncmp(); /* compare n characters */ 269: int subprojects(); /* check for subprojects */ 270: int xppath(); /* expand project pathname */ 271: PATH *pd; /* pathname struct pointer */ 272: PATH ppathbuf; /* parent project pathname struct buf */ 273: PATH *readpld(); /* read project link directory entry */ 274: PDB *openpdb(); /* open database */ 275: PDB *pldp; /* project link directory stream */ 276: void resetpdb(); /* reset current database pointer */ 277: 278: if (EQUAL(ppathname, CURPROJECT)) 279: { 280: warn("%s: can't remove current project", ppathname); 281: return(1); 282: } 283: plen = strlen(pb->p_path); 284: if (strncmp(pb->p_path, CWD, plen) == 0) 285: { 286: warn("can't remove %s from current directory", ppathname); 287: return(1); 288: } 289: if (FORCE) 290: { 291: if (*ppathname != _PDIRC && *ppathname != _HDIRC) 292: { 293: warn("%s must an absolute project pathname", ppathname); 294: return(1); 295: } 296: ppathhead(strcpy(pppathname, ppathname)); 297: } 298: else { 299: ppathcat(pppathname, ppathname, PARENTPROJECT); 300: } 301: if (xppath(pppathname, &ppathbuf) == -1) 302: { 303: patherr(""); 304: warn("force removal by typing `rmproject -F projectname'"); 305: return(1); 306: } 307: if ((pldp = openpdb(PLDNAME, ppathbuf.p_path, "rw")) == NULL) 308: return(errpdb((PDB *) NULL)); 309: while ((pd = readpld(pldp)) != NULL) 310: if (EQUAL(pb->p_alias, pd->p_alias)) 311: { 312: pstatus = subprojects(pb->p_path); 313: if (pstatus == 1) 314: { 315: warn("%s: project not empty", ppathname); 316: resetpdb(pldp); 317: break; 318: } 319: else if (pstatus == 2) 320: { 321: warn("can't remove %s because subprojects exist", 322: ppathname); 323: resetpdb(pldp); 324: break; 325: } 326: else if (pstatus == 3) 327: { 328: resetpdb(pldp); 329: break; 330: } 331: resetpdb(pldp); 332: } 333: else if (strncmp(pb->p_path, pd->p_path, plen) == 0) 334: { /* don't clobber nested projects */ 335: if (pd->p_mode == P_IFPROOT) 336: { 337: warnexist(ppathname, pd->p_alias); 338: pstatus = 4; 339: break; 340: } 341: } 342: else { 343: pputent(pldp); 344: } 345: if (pstatus != 0) 346: _closepdb(pldp); 347: else if ((pstatus = rmd(pb->p_path)) != 0) 348: _closepdb(pldp); 349: else 350: pstatus = closepdb(pldp); 351: return(pstatus != 0); 352: } 353: 354: 355: 356: /* 357: * rmtyp() removes type labels from an existing project directory. 358: */ 359: rmtyp(ppathname, pdirtyp, pb) 360: char *ppathname; /* project directory pathname */ 361: SLIST *pdirtyp; /* project directory type labels list */ 362: PATH *pb; /* pathname struct buffer */ 363: { 364: char *pbfndkey(); /* find key */ 365: int closepdb(); /* close database */ 366: int errpdb(); /* print database error */ 367: int pgetent(); /* load next entry into buffer */ 368: int pputent(); /* write buffer to database */ 369: int status = 0; /* return status */ 370: PDB *openpdb(); /* open database */ 371: PDB *pldp; /* project link directory stream */ 372: void pbrmtyp(); /* remove type labels from buffer */ 373: 374: if ((pldp = openpdb(PLDNAME, pb->p_project, "rw")) == NULL) 375: return(errpdb((PDB *) NULL)); 376: while (pgetent(pldp) != EOF) 377: { 378: if (pbfndkey(CURPROJECT) != NULL) 379: pbrmtyp(ppathname, pdirtyp); 380: pputent(pldp); 381: } 382: status = closepdb(pldp); 383: return(status); 384: } 385: 386: 387: 388: /* 389: * subprojects() returns 1 if a project has project directories, 2 if any 390: * of these are subprojects, and zero otherwise. 391: */ 392: subprojects(pathname) 393: char *pathname; /* project root directory pathname */ 394: { 395: char *pbfndkey(); /* find key */ 396: int closepdb(); /* close database */ 397: int errpdb(); /* print database error */ 398: int pbfndflag(); /* find flag field */ 399: int pgetent(); /* load next entry into buffer */ 400: int status = 0; /* return status */ 401: PDB *openpdb(); /* open database */ 402: PDB *pldp; /* project link directory stream */ 403: 404: if ((pldp = openpdb(PLDNAME, pathname, "r")) == NULL) 405: { 406: if (!FORCE) return(errpdb((PDB *) NULL)); 407: return(status); 408: } 409: while (pgetent(pldp) != EOF) 410: if (pbfndkey(CURPROJECT) == NULL && 411: pbfndkey(PARENTPROJECT) == NULL) 412: { 413: if (!RECURSIVE) status = 1; 414: if (pbfndflag(PROOTDIR) == YES) 415: { 416: status = 2; 417: break; 418: } 419: } 420: closepdb(pldp); 421: return(status); 422: } 423: 424: 425: 426: /* 427: * typargtolist() prepends comma-separated type labels specified in typarg 428: * to typlist. 429: */ 430: void 431: typargtolist(typarg, typlist) 432: register char *typarg; /* type labels argument */ 433: SLIST *typlist; /* type labels list */ 434: { 435: register char *t; /* type label argument pointer */ 436: char *slprepend(); /* prepend singly-linked list key */ 437: 438: for (t = typarg; *t != '\0'; t++) 439: continue; 440: for (; t >= typarg; t--) 441: if (t[0] == ',') 442: { 443: if (t[1] != '\0') 444: slprepend(t+1, typlist); 445: t[0] = '\0'; 446: } 447: slprepend(typarg, typlist); 448: } 449: 450: 451: 452: /* 453: * unproject() undefines a project root directory. Returns 0 is successful, 454: * otherwise 1. 455: */ 456: unproject(ppathname, pb) 457: char *ppathname; /* project root directory pathname */ 458: PATH *pb; /* pathname struct buffer */ 459: { 460: char *ppathcat(); /* project pathname concatenation */ 461: char *ppathhead(); /* remove tail of project pathname */ 462: char pppathname[PPATHSIZE]; /* parent project pathname */ 463: char *strcpy(); /* string copy */ 464: int _closepdb(); /* close database without updating */ 465: int closepdb(); /* close database */ 466: int errpdb(); /* print database error */ 467: int pputent(); /* write buffer to database */ 468: int pstatus = 0; /* project status */ 469: int rmpld(); /* remove project link directory */ 470: int subprojects(); /* check for subprojects */ 471: int xppath(); /* expand project pathname */ 472: PATH *pd; /* pathname struct pointer */ 473: PATH ppathbuf; /* parent project pathname struct buf */ 474: PATH *readpld(); /* read project link directory entry */ 475: PDB *openpdb(); /* open database */ 476: PDB *pldp; /* project link directory stream */ 477: void resetpdb(); /* reset current database pointer */ 478: 479: if (EQUAL(ppathname, CURPROJECT)) 480: { 481: warn("%s: can't undefine current project", ppathname); 482: return(1); 483: } 484: if (FORCE) 485: { 486: if (*ppathname != _PDIRC && *ppathname != _HDIRC) 487: { 488: warn("%s must an absolute project pathname", ppathname); 489: return(1); 490: } 491: ppathhead(strcpy(pppathname, ppathname)); 492: } 493: else { 494: ppathcat(pppathname, ppathname, PARENTPROJECT); 495: } 496: if (xppath(pppathname, &ppathbuf) == -1) 497: { 498: patherr(""); 499: warn("force conversion by typing `rmproject -uF projectname'"); 500: return(1); 501: } 502: if ((pldp = openpdb(PLDNAME, ppathbuf.p_path, "rw")) == NULL) 503: return(errpdb((PDB *) NULL)); 504: while ((pd = readpld(pldp)) != NULL) 505: if (EQUAL(pb->p_alias, pd->p_alias)) 506: { 507: pstatus = subprojects(pb->p_path); 508: if (pstatus == 1) 509: { 510: warn("can't undefine %s: project not empty", 511: ppathname); 512: resetpdb(pldp); 513: break; 514: } 515: else if (pstatus == 2) 516: { 517: warn("can't undefine %s because subprojects exist", 518: ppathname); 519: resetpdb(pldp); 520: break; 521: } 522: else if (pstatus == 3) 523: { 524: resetpdb(pldp); 525: break; 526: } 527: resetpdb(pldp); 528: } 529: else { 530: pputent(pldp); 531: } 532: if (pstatus != 0) 533: _closepdb(pldp); 534: else if ((pstatus = rmpld(pb->p_path)) != 0) 535: _closepdb(pldp); 536: else 537: pstatus = closepdb(pldp); 538: return(pstatus != 0); 539: } 540: 541: 542: 543: /* 544: * warnexist() warns of nested or duplicate projects. 545: */ 546: warnexist(ppathname, alias) 547: char *ppathname; /* project to be removed */ 548: char *alias; /* nested or duplicate project alias */ 549: { 550: char npathname[PPATHSIZE]; /* nested project pathname */ 551: char *p; /* nested project pathname pointer */ 552: char *strpcpy(); /* string copy and update pointer */ 553: 554: p = strpcpy(npathname, ppathname); 555: while (p > npathname && p[-1] != _PPSC) 556: p--; 557: *p = '\0'; 558: warn("can't remove %s because project %s%s exists", ppathname, 559: npathname, alias); 560: }