Annotation of mstools/mfc/src/dlgdata.cpp, revision 1.1

1.1     ! root        1: // This is a part of the Microsoft Foundation Classes C++ library.
        !             2: // Copyright (C) 1992 Microsoft Corporation
        !             3: // All rights reserved.
        !             4: 
        !             5: // This source code is only intended as a supplement to the 
        !             6: // Microsoft Foundation Classes Reference and Microsoft 
        !             7: // QuickHelp and/or WinHelp documentation provided with the library. 
        !             8: // See these sources for detailed information regarding the 
        !             9: // Microsoft Foundation Classes product. 
        !            10: 
        !            11: #include "stdafx.h"
        !            12: 
        !            13: #ifdef AFX_CORE3_SEG
        !            14: #pragma code_seg(AFX_CORE3_SEG)
        !            15: #endif
        !            16: 
        !            17: #ifdef _DEBUG
        !            18: #undef THIS_FILE
        !            19: static char BASED_CODE THIS_FILE[] = __FILE__;
        !            20: #endif
        !            21: 
        !            22: /////////////////////////////////////////////////////////////////////////////
        !            23: // CDataExchange member functions (contructor is in wincore.cpp for swap tuning)
        !            24: 
        !            25: HWND CDataExchange::PrepareEditCtrl(int nIDC)
        !            26: {
        !            27:        HWND hWndCtrl = PrepareCtrl(nIDC);
        !            28:        ASSERT(hWndCtrl != NULL);
        !            29:        m_bEditLastControl = TRUE;
        !            30:        return hWndCtrl;
        !            31: }
        !            32: 
        !            33: HWND CDataExchange::PrepareCtrl(int nIDC)
        !            34: {
        !            35:        ASSERT(nIDC != 0);
        !            36:        ASSERT(nIDC != -1); // not allowed
        !            37:        HWND hWndCtrl = ::GetDlgItem(m_pDlgWnd->m_hWnd, nIDC);
        !            38:        if (hWndCtrl == NULL)
        !            39:        {
        !            40:                TRACE1("Error: no data exchange control with ID 0x%04X\n", nIDC);
        !            41:                ASSERT(FALSE);
        !            42:                AfxThrowNotSupportedException();
        !            43:        }
        !            44:        m_hWndLastControl = hWndCtrl;
        !            45:        m_bEditLastControl = FALSE; // not an edit item by default
        !            46:        ASSERT(hWndCtrl != NULL);       // never return NULL handle
        !            47:        return hWndCtrl;
        !            48: }
        !            49: 
        !            50: void CDataExchange::Fail()
        !            51: {
        !            52:        if (!m_bSaveAndValidate)
        !            53:        {
        !            54:                TRACE0("Warning: CDataExchange::Fail called when not validating\n");
        !            55:                // throw the exception anyway
        !            56:        }
        !            57:        else if (m_hWndLastControl != NULL)
        !            58:        {
        !            59:                // restore focus and selection to offending field
        !            60:                ::SetFocus(m_hWndLastControl);
        !            61:                if (m_bEditLastControl) // select edit item
        !            62:                        ::SendMessage(m_hWndLastControl, EM_SETSEL, 0, -1);
        !            63:        }
        !            64:        else
        !            65:        {
        !            66:                TRACE0("Error: fail validation with no control to restore focus to\n");
        !            67:                // do nothing more
        !            68:        }
        !            69: 
        !            70:        AfxThrowUserException();
        !            71: }
        !            72: 
        !            73: /////////////////////////////////////////////////////////////////////////////
        !            74: // Notes for implementing dialog data exchange and validation procs:
        !            75: //  * always start with PrepareCtrl or PrepareEditCtrl
        !            76: //  * always start with 'pDX->m_bSaveAndValidate' check
        !            77: //  * pDX->Fail() will throw an exception - so be prepared
        !            78: //  * try to avoid creating temporary HWNDs for dialog controls - i.e.
        !            79: //          use HWNDs for child elements
        !            80: //  * validation procs should only act if 'm_bSaveAndValidate'
        !            81: //  * use the suffices:
        !            82: //          DDX_ = exchange proc
        !            83: //          DDV_ = validation proc
        !            84: //
        !            85: /////////////////////////////////////////////////////////////////////////////
        !            86: 
        !            87: // only supports '%d', '%u', '%ld' and '%lu'
        !            88: static BOOL PASCAL NEAR _AfxSimpleScanf(const char* pszText,
        !            89:        const char* pszFormat, va_list pData)
        !            90: {
        !            91:        ASSERT(pszText != NULL);
        !            92:        ASSERT(pszFormat != NULL);
        !            93: 
        !            94:        ASSERT(*pszFormat == '%');
        !            95:        pszFormat++;        // skip '%'
        !            96: 
        !            97:        BOOL bLong = FALSE;
        !            98:        if (*pszFormat == 'l')
        !            99:        {
        !           100:                bLong = TRUE;
        !           101:                pszFormat++;
        !           102:        }
        !           103: 
        !           104:        ASSERT(*pszFormat == 'd' || *pszFormat == 'u');
        !           105:        ASSERT(pszFormat[1] == '\0');
        !           106: 
        !           107:        while (*pszText == ' ' || *pszText == '\t')
        !           108:                pszText++;
        !           109:        char chFirst = pszText[0];
        !           110:        long l, l2;
        !           111:        if (*pszFormat == 'd')
        !           112:        {
        !           113:                // signed
        !           114:                l = strtol(pszText, (char**)&pszText, 10);
        !           115:                l2 = (int)l;
        !           116:        }
        !           117:        else
        !           118:        {
        !           119:                // unsigned
        !           120:                l = (long)strtoul(pszText, (char**)&pszText, 10);
        !           121:                l2 = (unsigned int)l;
        !           122:        }
        !           123:        if (l == 0 && chFirst != '0')
        !           124:                return FALSE;   // could not convert
        !           125: 
        !           126:        while (*pszText == ' ' || *pszText == '\t')
        !           127:                pszText++;
        !           128:        if (*pszText != '\0')
        !           129:                return FALSE;   // not terminated properly
        !           130: 
        !           131:        if (bLong)
        !           132:                *va_arg(pData, long*) = l;
        !           133:        else if (l == l2)
        !           134:                *va_arg(pData, int*) = (int)l;
        !           135:        else
        !           136:                return FALSE;       // too big for int
        !           137: 
        !           138:        // all ok
        !           139:        return TRUE;
        !           140: }
        !           141: 
        !           142: 
        !           143: static void PASCAL NEAR DDX_TextWithFormat(CDataExchange* pDX, int nIDC,
        !           144:        const char* pszFormat, UINT nIDPrompt, ...)
        !           145:        // only supports windows output formats - no floating point
        !           146: {
        !           147:        va_list pData;
        !           148:        va_start(pData, nIDPrompt);
        !           149:        
        !           150:        HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
        !           151:        char szT[64];
        !           152:        if (pDX->m_bSaveAndValidate)
        !           153:        {
        !           154:                // the following works for %d, %u, %ld, %lu
        !           155:                ::GetWindowText(hWndCtrl, szT, sizeof(szT));
        !           156:                if (!_AfxSimpleScanf(szT, pszFormat, pData))
        !           157:                {
        !           158:                        AfxMessageBox(nIDPrompt);
        !           159:                        pDX->Fail();        // throws exception
        !           160:                }
        !           161:        }
        !           162:        else
        !           163:        {
        !           164:                wvsprintf(szT, pszFormat, pData);
        !           165:                        // does not support floating point numbers - see dlgfloat.cpp
        !           166:                _AfxSmartSetWindowText(hWndCtrl, szT);
        !           167:        }
        !           168:        
        !           169:        va_end(pData);
        !           170: }
        !           171: 
        !           172: /////////////////////////////////////////////////////////////////////////////
        !           173: // Simple formatting to text item
        !           174: 
        !           175: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, int& value)
        !           176: {
        !           177:        if (pDX->m_bSaveAndValidate)
        !           178:                DDX_TextWithFormat(pDX, nIDC, "%d", AFX_IDP_PARSE_INT, &value);
        !           179:        else
        !           180:                DDX_TextWithFormat(pDX, nIDC, "%d", AFX_IDP_PARSE_INT, value);  
        !           181: }
        !           182: 
        !           183: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, UINT& value)
        !           184: {
        !           185:        if (pDX->m_bSaveAndValidate)
        !           186:                DDX_TextWithFormat(pDX, nIDC, "%u", AFX_IDP_PARSE_INT, &value);
        !           187:        else
        !           188:                DDX_TextWithFormat(pDX, nIDC, "%u", AFX_IDP_PARSE_INT, value);
        !           189: }
        !           190: 
        !           191: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value)
        !           192: {
        !           193:        if (pDX->m_bSaveAndValidate)
        !           194:                DDX_TextWithFormat(pDX, nIDC, "%ld", AFX_IDP_PARSE_INT, &value);
        !           195:        else
        !           196:                DDX_TextWithFormat(pDX, nIDC, "%ld", AFX_IDP_PARSE_INT, value);
        !           197: }
        !           198: 
        !           199: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, DWORD& value)
        !           200: {
        !           201:        if (pDX->m_bSaveAndValidate)
        !           202:                DDX_TextWithFormat(pDX, nIDC, "%lu", AFX_IDP_PARSE_INT, &value);
        !           203:        else
        !           204:                DDX_TextWithFormat(pDX, nIDC, "%lu", AFX_IDP_PARSE_INT, value);
        !           205: }
        !           206: 
        !           207: void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value)
        !           208: {
        !           209:        HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
        !           210:        if (pDX->m_bSaveAndValidate)
        !           211:        {
        !           212:                int nLen = ::GetWindowTextLength(hWndCtrl);
        !           213:                ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
        !           214:        }
        !           215:        else
        !           216:        {
        !           217:                _AfxSmartSetWindowText(hWndCtrl, value);
        !           218:        }
        !           219: }
        !           220: 
        !           221: /////////////////////////////////////////////////////////////////////////////
        !           222: // Data exchange for special control
        !           223: 
        !           224: void AFXAPI DDX_Check(CDataExchange* pDX, int nIDC, int& value)
        !           225: {
        !           226:        HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
        !           227:        if (pDX->m_bSaveAndValidate)
        !           228:        {
        !           229:                value = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);
        !           230:                ASSERT(value >= 0 && value <= 2);
        !           231:        }
        !           232:        else
        !           233:        {
        !           234:                if (value < 0 || value > 2)
        !           235:                {
        !           236:                        value = 0;      // default to off
        !           237:                        TRACE1("Warning: dialog data checkbox value out of range"
        !           238:                                        " %d\n", value);
        !           239:                }
        !           240:                ::SendMessage(hWndCtrl, BM_SETCHECK, (WPARAM)value, 0L);
        !           241:        }
        !           242: }
        !           243: 
        !           244: void AFXAPI DDX_Radio(CDataExchange* pDX, int nIDC, int& value)
        !           245:        // must be first in a group of auto radio buttons
        !           246: {
        !           247:        HWND hWndFirstCtrl = pDX->PrepareCtrl(nIDC);
        !           248: 
        !           249:        ASSERT(::GetWindowLong(hWndFirstCtrl, GWL_STYLE) & WS_GROUP);
        !           250:        ASSERT((::GetWindowLong(hWndFirstCtrl, GWL_STYLE) & 0xf)
        !           251:                        == BS_AUTORADIOBUTTON);
        !           252: 
        !           253:        if (pDX->m_bSaveAndValidate)
        !           254:                value = -1;         // value if none found
        !           255: 
        !           256:        // walk all children in group
        !           257:        HWND hWndCtrl = hWndFirstCtrl;
        !           258:        int iButton = 0;
        !           259:        do
        !           260:        {
        !           261:                if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
        !           262:                {
        !           263:                        // control in group is a radio button
        !           264:                        if (pDX->m_bSaveAndValidate)
        !           265:                        {
        !           266:                                if (::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)
        !           267:                                {
        !           268:                                        ASSERT(value == -1);    // only set once
        !           269:                                        value = iButton;
        !           270:                                }
        !           271:                        }
        !           272:                        else
        !           273:                        {
        !           274:                                // select button
        !           275:                                ::SendMessage(hWndCtrl, BM_SETCHECK, (iButton == value), 0L);
        !           276:                        }
        !           277:                        iButton++;
        !           278:                }
        !           279:                else
        !           280:                {
        !           281:                        TRACE0("Warning: skipping non-radio button in group\n");
        !           282:                }
        !           283:                hWndCtrl = ::GetNextDlgGroupItem(pDX->m_pDlgWnd->m_hWnd,
        !           284:                        hWndCtrl, FALSE);       // get next
        !           285:        } while (hWndCtrl != hWndFirstCtrl);
        !           286: }
        !           287: 
        !           288: /////////////////////////////////////////////////////////////////////////////
        !           289: // Listboxes, comboboxes
        !           290: 
        !           291: void AFXAPI DDX_LBString(CDataExchange* pDX, int nIDC, CString& value)
        !           292: {
        !           293:        HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
        !           294:        if (pDX->m_bSaveAndValidate)
        !           295:        {
        !           296:                int nIndex = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
        !           297:                if (nIndex != -1)
        !           298:                {
        !           299:                        int nLen = (int)::SendMessage(hWndCtrl, LB_GETTEXTLEN, nIndex, 0L);
        !           300:                        ::SendMessage(hWndCtrl, LB_GETTEXT, nIndex,
        !           301:                                        (LPARAM)(LPSTR)value.GetBufferSetLength(nLen));
        !           302:                }
        !           303:                else
        !           304:                {
        !           305:                        // no selection
        !           306:                        value.Empty();
        !           307:                }
        !           308:        }
        !           309:        else
        !           310:        {
        !           311:                // set current selection based on data string
        !           312:                if (::SendMessage(hWndCtrl, LB_SELECTSTRING, (WPARAM)-1,
        !           313:                  (LPARAM)(LPCSTR)value) == LB_ERR)
        !           314:                {
        !           315:                        // no selection match
        !           316:                        TRACE0("Warning: no listbox item selected\n");
        !           317:                }
        !           318:        }
        !           319: }
        !           320: 
        !           321: // Win 3.1 only
        !           322: void AFXAPI DDX_LBStringExact(CDataExchange* pDX, int nIDC, CString& value)
        !           323: {
        !           324:        HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
        !           325:        if (pDX->m_bSaveAndValidate)
        !           326:        {
        !           327:                DDX_LBString(pDX, nIDC, value);
        !           328:        }
        !           329:        else
        !           330:        {
        !           331:                // set current selection based on data string
        !           332:                int i = (int)::SendMessage(hWndCtrl, LB_FINDSTRINGEXACT, (WPARAM)-1,
        !           333:                  (LPARAM)(LPCSTR)value);
        !           334:                if (i < 0)
        !           335:                {
        !           336:                        // no selection match
        !           337:                        TRACE0("Warning: no listbox item selected\n");
        !           338:                }
        !           339:                else
        !           340:                {
        !           341:                        // select it
        !           342:                        SendMessage(hWndCtrl, LB_SETCURSEL, i, 0L);
        !           343:                }
        !           344:        }
        !           345: }
        !           346: 
        !           347: void AFXAPI DDX_CBString(CDataExchange* pDX, int nIDC, CString& value)
        !           348: {
        !           349:        HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
        !           350:        if (pDX->m_bSaveAndValidate)
        !           351:        {
        !           352:                // just get current edit item text (or drop list static)
        !           353:                int nLen = ::GetWindowTextLength(hWndCtrl);
        !           354:                if (nLen != -1)
        !           355:                {
        !           356:                        // get known length
        !           357:                        ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
        !           358:                }
        !           359:                else
        !           360:                {
        !           361:                        // for drop lists GetWindowTextLength does not work - assume
        !           362:                        //  max of 255 characters
        !           363:                        ::GetWindowText(hWndCtrl, value.GetBuffer(255), 255+1);
        !           364:                        value.ReleaseBuffer();
        !           365:                }
        !           366:        }
        !           367:        else
        !           368:        {
        !           369:                // set current selection based on model string
        !           370:                if (::SendMessage(hWndCtrl, CB_SELECTSTRING, (WPARAM)-1,
        !           371:                        (LPARAM)(LPCSTR)value) == CB_ERR)
        !           372:                {
        !           373:                        // just set the edit text (will be ignored if DROPDOWNLIST)
        !           374:                        _AfxSmartSetWindowText(hWndCtrl, value);
        !           375:                }
        !           376:        }
        !           377: }
        !           378: 
        !           379: // Win 3.1 only
        !           380: void AFXAPI DDX_CBStringExact(CDataExchange* pDX, int nIDC, CString& value)
        !           381: {
        !           382:        HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
        !           383:        if (pDX->m_bSaveAndValidate)
        !           384:        {
        !           385:                DDX_CBString(pDX, nIDC, value);
        !           386:        }
        !           387:        else
        !           388:        {
        !           389:                // set current selection based on data string
        !           390:                int i = (int)::SendMessage(hWndCtrl, CB_FINDSTRINGEXACT, (WPARAM)-1,
        !           391:                  (LPARAM)(LPCSTR)value);
        !           392:                if (i < 0)
        !           393:                {
        !           394:                        // no selection match
        !           395:                        TRACE0("Warning: no combobox item selected\n");
        !           396:                }
        !           397:                else
        !           398:                {
        !           399:                        // select it
        !           400:                        SendMessage(hWndCtrl, CB_SETCURSEL, i, 0L);
        !           401:                }
        !           402:        }
        !           403: }
        !           404: 
        !           405: void AFXAPI DDX_LBIndex(CDataExchange* pDX, int nIDC, int& index)
        !           406: {
        !           407:        HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
        !           408:        if (pDX->m_bSaveAndValidate)
        !           409:                index = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
        !           410:        else
        !           411:                ::SendMessage(hWndCtrl, LB_SETCURSEL, (WPARAM)index, 0L);
        !           412: }
        !           413: 
        !           414: void AFXAPI DDX_CBIndex(CDataExchange* pDX, int nIDC, int& index)
        !           415: {
        !           416:        HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
        !           417:        if (pDX->m_bSaveAndValidate)
        !           418:                index = (int)::SendMessage(hWndCtrl, CB_GETCURSEL, 0, 0L);
        !           419:        else
        !           420:                ::SendMessage(hWndCtrl, CB_SETCURSEL, (WPARAM)index, 0L);
        !           421: }
        !           422: 
        !           423: /////////////////////////////////////////////////////////////////////////////
        !           424: // Range Dialog Data Validation
        !           425: 
        !           426: static void NEAR PASCAL FailMinMaxWithFormat(CDataExchange* pDX,
        !           427:         long minVal, long maxVal, const char* pszFormat, UINT nIDPrompt)
        !           428:        // error string must have '%1' and '%2' strings for min and max values
        !           429:        // wsprintf formatting uses long values (format should be '%ld' or '%lu')
        !           430: {
        !           431:        ASSERT(pszFormat != NULL);
        !           432: 
        !           433:        if (!pDX->m_bSaveAndValidate)
        !           434:        {
        !           435:                TRACE0("Warning: initial dialog data is out of range\n");
        !           436:                return;     // don't stop now
        !           437:        }
        !           438:        char szMin[32];
        !           439:        char szMax[32];
        !           440:        wsprintf(szMin, pszFormat, minVal);
        !           441:        wsprintf(szMax, pszFormat, maxVal);
        !           442:        CString prompt;
        !           443:        AfxFormatString2(prompt, nIDPrompt, szMin, szMax);
        !           444:        AfxMessageBox(prompt, MB_ICONEXCLAMATION, nIDPrompt);
        !           445:        prompt.Empty(); // exception prep
        !           446:        pDX->Fail();
        !           447: }
        !           448: 
        !           449: //NOTE: don't use overloaded function names to avoid type ambiguities
        !           450: void AFXAPI DDV_MinMaxInt(CDataExchange* pDX, int value, int minVal, int maxVal)
        !           451: {
        !           452:        ASSERT(minVal <= maxVal);
        !           453:        if (value < minVal || value > maxVal)
        !           454:                FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%ld",
        !           455:                        AFX_IDP_PARSE_INT_RANGE);
        !           456: }
        !           457: 
        !           458: void AFXAPI DDV_MinMaxLong(CDataExchange* pDX, long value, long minVal, long maxVal)
        !           459: {
        !           460:        ASSERT(minVal <= maxVal);
        !           461:        if (value < minVal || value > maxVal)
        !           462:                FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%ld",
        !           463:                        AFX_IDP_PARSE_INT_RANGE);
        !           464: }
        !           465: 
        !           466: void AFXAPI DDV_MinMaxUInt(CDataExchange* pDX, UINT value, UINT minVal, UINT maxVal)
        !           467: {
        !           468:        ASSERT(minVal <= maxVal);
        !           469:        if (value < minVal || value > maxVal)
        !           470:                FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%lu",
        !           471:                        AFX_IDP_PARSE_INT_RANGE);
        !           472: }
        !           473: 
        !           474: void AFXAPI DDV_MinMaxDWord(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal)
        !           475: {
        !           476:        ASSERT(minVal <= maxVal);
        !           477:        if (value < minVal || value > maxVal)
        !           478:                FailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, "%lu",
        !           479:                        AFX_IDP_PARSE_INT_RANGE);
        !           480: }
        !           481: 
        !           482: /////////////////////////////////////////////////////////////////////////////
        !           483: // Max Chars Dialog Data Validation
        !           484: 
        !           485: void AFXAPI DDV_MaxChars(CDataExchange* pDX, CString const& value, int nChars)
        !           486: {
        !           487:        ASSERT(nChars >= 1);        // allow them something
        !           488:        if (pDX->m_bSaveAndValidate && value.GetLength() > nChars)
        !           489:        {
        !           490:                char szT[32];
        !           491:                wsprintf(szT, "%d", nChars);
        !           492:                CString prompt;
        !           493:                AfxFormatString1(prompt, AFX_IDP_PARSE_STRING_SIZE, szT);
        !           494:                AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_STRING_SIZE);
        !           495:                prompt.Empty(); // exception prep
        !           496:                pDX->Fail();
        !           497:        }
        !           498: }
        !           499: 
        !           500: /////////////////////////////////////////////////////////////////////////////
        !           501: // Special DDX_ proc for subclassing controls
        !           502: 
        !           503: void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
        !           504: {
        !           505:        if (rControl.m_hWnd == NULL)    // not subclassed yet
        !           506:        {
        !           507:                ASSERT(!pDX->m_bSaveAndValidate);
        !           508:                HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
        !           509:                if (!rControl.SubclassWindow(hWndCtrl))
        !           510:                {
        !           511:                        ASSERT(FALSE);      // possibly trying to subclass twice ?
        !           512:                        AfxThrowNotSupportedException();
        !           513:                }
        !           514:        }
        !           515: }
        !           516: 
        !           517: /////////////////////////////////////////////////////////////////////////////

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.