why does del /q "" work like del /q *.* and ruin me

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
nnnmmm
Posts: 141
Joined: 26 Aug 2017 06:11

why does del /q "" work like del /q *.* and ruin me

#1 Post by nnnmmm » 12 Aug 2023 20:28

SET MA=%~dp0base\flashlight mod.pk4
del /q "%MA%"

renaming and sorting variable names, i only changed MA to X1 in SET, then a disater stoke, being shocked and heartbroken
del /q %MA%", was treated as del /q ""
why does del /q "" work like del /q *.* and delete everything in its current directory
maybe can someone explain it to me in few words and tips but long enough for me never to forget

what about the case of RMDIR /s /q "%YY1%" when YY1 is NOT claimed

now i have to use DEL and RD like this way
if /I exist "%X1%" del /q "%X1%"
if /I exist "%YY1%" RMDIR /s /q "%YY1%"

what other disasters are maybe waiting to happen?
del /q
del /q "
del /q ^
del /q (
del /q ** i dont know i just made up these commands

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: why does del /q "" work like del /q *.* and ruin me

#2 Post by dbenham » 27 Aug 2023 07:14

Ouch! It has been a long while since I have spent much time with batch scripts or Windows command line, but I don't remember ever seeing this abomination.

I ran some additional tests, and all of the following are treated like DEL *

Code: Select all

del ""
del "
del """"""
del .
del "."
del ".
My guess is that all the above are treated like an empty string, and for some unknown reason, DEL treats an empty string like *

The only other command I have seen behave similarly is DIR. I tried with RD/RMDIR, REN/RENAME, COPY, XCOPY, and TYPE, and none of those commands were afflicted with that disastrous design.

But there is a major difference between DIR and DEL - the filename argument is optional for DIR, but required for DEL. So the behavior makes sense for DIR.

I can't imagine how MS could have ended up with this disaster. If you had to have this "feature", DEL is probably the worst command possible - so destructive.

mataha
Posts: 35
Joined: 27 Apr 2023 12:34

Re: why does del /q "" work like del /q *.* and ruin me

#3 Post by mataha » 27 Aug 2023 08:18

I had a quick look at the available source code, but didn't see anything at the first glance:

Code: Select all

int eDelete(struct cmdnode *n) {
    return(LastRetCode = DelWork(n->argptr));
}

Code: Select all

int DelWork(TCHAR *pszCmdLine) {
    DRP drpCur = {0};
    TCHAR szCurDrv[MAX_PATH + 2];
    ULONG OldDCount;
    STATUS rc;

    OldDCount = DCount;

    drpCur.rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
    drpCur.rgfAttribsOnOff = 0;
    drpCur.cpatdsc = 0;
    drpCur.dwTimeType = LAST_WRITE_TIME;

    if (ParseDelParms(pszCmdLine, &drpCur) == FAILURE) {
        return(FAILURE);
    }

    GetDir((PTCHAR)szCurDrv, GD_DEFAULT);
    if (drpCur.cpatdsc == 0) {
        PutStdErr(MSG_BAD_SYNTAX, NOARGS);
        return(FAILURE);
    }

    drpCur.rgfSwitches |= DELPROCESSEARLY;
    rc = DelPatterns(&drpCur);

    mystrcpy(CurDrvDir, szCurDrv);
    FreeStack(OldDCount);
    return((int)rc);
}
I'd probably look for the culprit in the method that's responsible for parsing parameters to del:

Code: Select all

STATUS ParseDelParms(IN PTCHAR pszCmdLine, OUT PDRP pdrp) {
    PTCHAR pszTok;
    TCHAR szT[10];
    USHORT irgchTok;
    BOOLEAN fToggle;
    PPATDSC ppatdscCur;

    szT[0] = TEXT('/');
    szT[1] = TEXT('\0');
    pszTok = TokStr(pszCmdLine, szT, TS_SDTOKENS);

    ppatdscCur = &(pdrp->patdscFirst);
    if (pdrp->cpatdsc) {
        while (ppatdscCur->ppatdscNext) {
            ppatdscCur = ppatdscCur->ppatdscNext;
        }
    }

    for (irgchTok = 0; *pszTok; pszTok += mystrlen(pszTok) + 1, irgchTok = 0) {
        fToggle = FALSE;
        if (pszTok[irgchTok] == (TCHAR) TEXT('/')) {
            if (pszTok[irgchTok + 2] == TEXT('-')) {
                fToggle = TRUE;
                irgchTok++;
            }
            switch (_totupper(pszTok[irgchTok + 2])) {
                case TEXT('P'):
                    fToggle ? (pdrp->rgfSwitches ^= PROMPTUSERSWITCH)
                            : (pdrp->rgfSwitches |= PROMPTUSERSWITCH);
                    if (pszTok[irgchTok + 3]) goto failure;
                    break;
                case TEXT('S'):
                    fToggle ? (pdrp->rgfSwitches ^= RECURSESWITCH)
                            : (pdrp->rgfSwitches |= RECURSESWITCH);
                    if (pszTok[irgchTok + 3]) goto failure;
                    break;
                case TEXT('F'):
                    fToggle ? (pdrp->rgfSwitches ^= FORCEDELSWITCH)
                            : (pdrp->rgfSwitches |= FORCEDELSWITCH);
                    if (pszTok[irgchTok + 3]) goto failure;
                    break;
                case TEXT('?'):
                    BeginHelpPause();
                    PutStdOut(MSG_HELP_DEL_ERASE, NOARGS);
                    EndHelpPause();
                    return(FAILURE);
                    break;
                case TEXT('Q'):
                    fToggle ? (pdrp->rgfSwitches ^= QUIETSWITCH)
                            : (pdrp->rgfSwitches |= QUIETSWITCH);
                    if (pszTok[irgchTok + 3]) goto failure;
                    break;
                case TEXT('A'):
                    if (fToggle) {
                        if (_tcslen(&(pszTok[irgchTok + 2])) > 1) goto failure;
                        pdrp->rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
                        pdrp->rgfAttribsOnOff = 0;
                        break;
                    }
                    if (SetAttribs(&(pszTok[irgchTok + 3]), pdrp)) {
                        return(FAILURE);
                    }
                    if (pdrp->rgfAttribsOnOff & FILE_ATTRIBUTE_READONLY) {
                        pdrp->rgfSwitches |= FORCEDELSWITCH;
                    }
                    break;
                default:
                    szT[0] = TEXT('/');
                    szT[1] = pszTok[2];
                    szT[2] = TEXT('\0');
                    PutStdErr(MSG_INVALID_SWITCH, ONEARG, (UINT_PTR)(&(pszTok[irgchTok + 2])));
                    return(FAILURE);
            }
            pszTok += 2;
        } else {
            if (pdrp->cpatdsc) {
                ppatdscCur->ppatdscNext = (PPATDSC)gmkstr(sizeof(PATDSC));
                ppatdscCur = ppatdscCur->ppatdscNext;
                ppatdscCur->ppatdscNext = NULL;
            }
            pdrp->cpatdsc++;
            ppatdscCur->pszPattern = (PTCHAR)gmkstr(_tcslen(pszTok)*sizeof(TCHAR) + sizeof(TCHAR));
            mystrcpy(ppatdscCur->pszPattern, StripQuotes(pszTok));
            ppatdscCur->fIsFat = TRUE;
        }
    }
    return(SUCCESS);
failure:
    PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG, (UINT_PTR)(&(pszTok[irgchTok + 2])));
    return(FAILURE);
}
Or del's pattern parser:

Code: Select all

STATUS DelPatterns(IN PDRP pdpr) {
    PPATDSC ppatdscCur, ppatdscX;
    PFS pfsCur, pfsFirst;
    ULONG i;
    STATUS rc;
    ULONG cffTotal = 0;
    TCHAR szSearchPath[MAX_PATH + 2];

    DosErr = 0;
    if (BuildFSFromPatterns(pdpr, TRUE, FALSE, &pfsFirst) == FAILURE) {
        return(FAILURE);
    }
    for (pfsCur = pfsFirst; pfsCur; pfsCur = pfsCur->pfsNext) {
        rc = WalkTree(pfsCur,
                      NULL,
                      pdpr->rgfAttribs,
                      pdpr->rgfAttribsOnOff,
                      (BOOLEAN)(pdpr->rgfSwitches & RECURSESWITCH),
                      pdpr,
                      NULL,
                      NULL,
                      NewEraseFile,
                      NULL);
        if (rc == FAILURE) {
            return rc;
        }
        if (rc != SUCCESS && rc != ERROR_FILE_NOT_FOUND) {
            PutStdErr(rc, NOARGS);
            return rc;
        }
        if (rc == ERROR_FILE_NOT_FOUND) {
            rc = AppendPath(szSearchPath,
                            sizeof(szSearchPath) / sizeof(TCHAR),
                            pfsCur->pszDir,
                            pfsCur->ppatdsc->pszPattern);
            if (rc == SUCCESS) {
                PutStdErr(MSG_NOT_FOUND, ONEARG, szSearchPath);
            }
        }
        FreeStr(pfsCur->pszDir);
        for (i = 1, ppatdscCur = pfsCur->ppatdsc;
             i <= pfsCur->cpatdsc;
             i++, ppatdscCur = ppatdscX) {
            ppatdscX = ppatdscCur->ppatdscNext;
            FreeStr(ppatdscCur->pszPattern);
            FreeStr(ppatdscCur->pszDir);
            FreeStr((PTCHAR)ppatdscCur);
        }
    }
    return(rc);
}

nnnmmm
Posts: 141
Joined: 26 Aug 2017 06:11

Re: why does del /q "" work like del /q *.* and ruin me

#4 Post by nnnmmm » 22 Sep 2023 06:28

> seeing this abomination.......I can't imagine how MS could have ended up with this disaster. DEL is probably the worst command possible - so destructive.

your statement is gonna make me not forget the damn DEL thing

i didnt get a response for this issue maybe for 2 weeks, so i thought it could just be something that was too obvious to pro's, but not to someone much less. so i moved on... but now

oh yeah!! worst and so destructive abomination, right on!!!

Post Reply