Changeset 212 for trunk/src/helpers


Ignore:
Timestamp:
Aug 22, 2002, 9:45:41 PM (23 years ago)
Author:
umoeller
Message:

Misc changes.

Location:
trunk/src/helpers
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/helpers/apps.c

    r209 r212  
    801801 *
    802802 *@@added V0.9.20 (2002-07-03) [umoeller]
     803 *@@changed V0.9.21 (2002-08-21) [umoeller]: now allowing for UNC
    803804 */
    804805
     
    811812    // check if the executable is fully qualified; if so,
    812813    // check if the executable file exists
    813     if (    (pDetails->pszExecutable[1] == ':')
    814          && (strchr(pDetails->pszExecutable, '\\'))
     814    if (    // allow UNC V0.9.21 (2002-08-21) [umoeller]
     815            (    (pDetails->pszExecutable[0] == '\\')
     816              && (pDetails->pszExecutable[1] == '\\')
     817            )
     818         || (    (pDetails->pszExecutable[1] == ':')
     819              && (strchr(pDetails->pszExecutable, '\\'))
     820            )
    815821       )
    816822    {
     
    11801186
    11811187    PROGDETAILS     Details;
     1188    ULONG           ulIsWinApp;
     1189
     1190    // parameter checking extended V0.9.21 (2002-08-21) [umoeller]
     1191    if (    (!pcProgDetails)
     1192         || (!pcProgDetails->pszExecutable)
     1193         || (!pcProgDetails->pszExecutable[0])
     1194         || (!ppDetails)
     1195       )
     1196        return ERROR_INVALID_PARAMETER;
    11821197
    11831198    *ppDetails = NULL;
    1184 
    1185     if (!pcProgDetails && !ppDetails)
    1186         return ERROR_INVALID_PARAMETER;
    11871199
    11881200    /*
     
    11991211    Details.progt.fbVisible = SHE_VISIBLE;
    12001212
    1201     // all this only makes sense if this contains something...
    1202     // besides, this crashed on string comparisons V0.9.9 (2001-01-27) [umoeller]
    1203     if (    (!Details.pszExecutable)
    1204          || (!Details.pszExecutable[0])
     1213    // memset(&Details.swpInitial, 0, sizeof(SWP));
     1214    // this wasn't a good idea... WPProgram stores stuff
     1215    // in here, such as the "minimize on startup" -> SWP_MINIMIZE
     1216
     1217    // duplicate parameters...
     1218    // we need this for string manipulations below...
     1219    if (    (Details.pszParameters)
     1220         && (Details.pszParameters[0])    // V0.9.18
    12051221       )
    1206         arc = ERROR_INVALID_PARAMETER;
    1207     else
    1208     {
    1209         ULONG           ulIsWinApp;
    1210 
    1211         // memset(&Details.swpInitial, 0, sizeof(SWP));
    1212         // this wasn't a good idea... WPProgram stores stuff
    1213         // in here, such as the "minimize on startup" -> SWP_MINIMIZE
    1214 
    1215         // duplicate parameters...
    1216         // we need this for string manipulations below...
    1217         if (    (Details.pszParameters)
    1218              && (Details.pszParameters[0])    // V0.9.18
    1219            )
    1220             xstrcpy(&strParamsPatched,
    1221                     Details.pszParameters,
    1222                     0);
    1223 
    1224         #ifdef DEBUG_PROGRAMSTART
    1225             _PmpfF((" old progc: 0x%lX", pcProgDetails->progt.progc));
    1226             _Pmpf(("  pszTitle: %s", STRINGORNULL(Details.pszTitle)));
    1227             _Pmpf(("  pszExecutable: %s", STRINGORNULL(Details.pszExecutable)));
    1228             _Pmpf(("  pszParameters: %s", STRINGORNULL(Details.pszParameters)));
    1229             _Pmpf(("  pszIcon: %s", STRINGORNULL(Details.pszIcon)));
    1230         #endif
    1231 
    1232         // program type fixups
    1233         switch (Details.progt.progc)        // that's a ULONG
    1234         {
    1235             case ((ULONG)-1):       // we get that sometimes...
    1236             case PROG_DEFAULT:
    1237             {
    1238                 // V0.9.12 (2001-05-26) [umoeller]
    1239                 ULONG ulDosAppType;
    1240                 appQueryAppType(Details.pszExecutable,
    1241                                 &ulDosAppType,
    1242                                 &Details.progt.progc);
    1243             }
    1244             break;
    1245         }
    1246 
    1247         // set session type from option flags
     1222        xstrcpy(&strParamsPatched,
     1223                Details.pszParameters,
     1224                0);
     1225
     1226    #ifdef DEBUG_PROGRAMSTART
     1227        _PmpfF((" old progc: 0x%lX", pcProgDetails->progt.progc));
     1228        _Pmpf(("  pszTitle: %s", STRINGORNULL(Details.pszTitle)));
     1229        _Pmpf(("  pszExecutable: %s", STRINGORNULL(Details.pszExecutable)));
     1230        _Pmpf(("  pszParameters: %s", STRINGORNULL(Details.pszParameters)));
     1231        _Pmpf(("  pszIcon: %s", STRINGORNULL(Details.pszIcon)));
     1232    #endif
     1233
     1234    // program type fixups
     1235    switch (Details.progt.progc)        // that's a ULONG
     1236    {
     1237        case ((ULONG)-1):       // we get that sometimes...
     1238        case PROG_DEFAULT:
     1239        {
     1240            // V0.9.12 (2001-05-26) [umoeller]
     1241            ULONG ulDosAppType;
     1242            appQueryAppType(Details.pszExecutable,
     1243                            &ulDosAppType,
     1244                            &Details.progt.progc);
     1245        }
     1246        break;
     1247    }
     1248
     1249    // set session type from option flags
     1250    if (ulFlags & APP_RUN_FULLSCREEN)
     1251    {
     1252        if (Details.progt.progc == PROG_WINDOWABLEVIO)
     1253            Details.progt.progc = PROG_FULLSCREEN;
     1254        else if (Details.progt.progc == PROG_WINDOWEDVDM)
     1255            Details.progt.progc = PROG_VDM;
     1256    }
     1257
     1258    if (ulIsWinApp = appIsWindowsApp(Details.progt.progc))
     1259    {
    12481260        if (ulFlags & APP_RUN_FULLSCREEN)
    1249         {
    1250             if (Details.progt.progc == PROG_WINDOWABLEVIO)
    1251                 Details.progt.progc = PROG_FULLSCREEN;
    1252             else if (Details.progt.progc == PROG_WINDOWEDVDM)
    1253                 Details.progt.progc = PROG_VDM;
    1254         }
    1255 
    1256         if (ulIsWinApp = appIsWindowsApp(Details.progt.progc))
    1257         {
    1258             if (ulFlags & APP_RUN_FULLSCREEN)
    1259                 Details.progt.progc = (ulFlags & APP_RUN_ENHANCED)
    1260                                                 ? PROG_31_ENH
    1261                                                 : PROG_31_STD;
    1262             else
    1263             {
    1264                 if (ulFlags & APP_RUN_STANDARD)
    1265                     Details.progt.progc = (ulFlags & APP_RUN_SEPARATE)
    1266                                                 ? PROG_31_STDSEAMLESSVDM
    1267                                                 : PROG_31_STDSEAMLESSCOMMON;
    1268                 else if (ulFlags & APP_RUN_ENHANCED)
    1269                     Details.progt.progc = (ulFlags & APP_RUN_SEPARATE)
    1270                                                 ? PROG_31_ENHSEAMLESSVDM
    1271                                                 : PROG_31_ENHSEAMLESSCOMMON;
    1272             }
    1273 
    1274             // re-run V0.9.16 (2001-10-19) [umoeller]
    1275             ulIsWinApp = appIsWindowsApp(Details.progt.progc);
    1276         }
    1277 
     1261            Details.progt.progc = (ulFlags & APP_RUN_ENHANCED)
     1262                                            ? PROG_31_ENH
     1263                                            : PROG_31_STD;
     1264        else
     1265        {
     1266            if (ulFlags & APP_RUN_STANDARD)
     1267                Details.progt.progc = (ulFlags & APP_RUN_SEPARATE)
     1268                                            ? PROG_31_STDSEAMLESSVDM
     1269                                            : PROG_31_STDSEAMLESSCOMMON;
     1270            else if (ulFlags & APP_RUN_ENHANCED)
     1271                Details.progt.progc = (ulFlags & APP_RUN_SEPARATE)
     1272                                            ? PROG_31_ENHSEAMLESSVDM
     1273                                            : PROG_31_ENHSEAMLESSCOMMON;
     1274        }
     1275
     1276        // re-run V0.9.16 (2001-10-19) [umoeller]
     1277        ulIsWinApp = appIsWindowsApp(Details.progt.progc);
     1278    }
     1279
     1280    /*
     1281     * command lines fixups:
     1282     *
     1283     */
     1284
     1285    if (!strcmp(Details.pszExecutable, "*"))
     1286    {
    12781287        /*
    1279          * command lines fixups:
     1288         * "*" for command sessions:
    12801289         *
    12811290         */
    12821291
    1283         if (!strcmp(Details.pszExecutable, "*"))
    1284         {
    1285             /*
    1286              * "*" for command sessions:
    1287              *
    1288              */
    1289 
    1290             if (ulIsWinApp)
     1292        if (ulIsWinApp)
     1293        {
     1294            // cheat: WinStartApp doesn't support NULL
     1295            // for Win-OS2 sessions, so manually start winos2.com
     1296            Details.pszExecutable = "WINOS2.COM";
     1297            // this is a DOS app, so fix this to DOS fullscreen
     1298            Details.progt.progc = PROG_VDM;
     1299
     1300            if (ulIsWinApp == 2)
    12911301            {
    1292                 // cheat: WinStartApp doesn't support NULL
    1293                 // for Win-OS2 sessions, so manually start winos2.com
    1294                 Details.pszExecutable = "WINOS2.COM";
    1295                 // this is a DOS app, so fix this to DOS fullscreen
    1296                 Details.progt.progc = PROG_VDM;
    1297 
    1298                 if (ulIsWinApp == 2)
     1302                // enhanced Win-OS/2 session:
     1303                PSZ psz = NULL;
     1304                if (strParamsPatched.ulLength)
     1305                    // "/3 " + existing params
     1306                    psz = strdup(strParamsPatched.psz);
     1307
     1308                xstrcpy(&strParamsPatched, "/3 ", 0);
     1309
     1310                if (psz)
    12991311                {
    1300                     // enhanced Win-OS/2 session:
    1301                     PSZ psz = NULL;
    1302                     if (strParamsPatched.ulLength)
    1303                         // "/3 " + existing params
    1304                         psz = strdup(strParamsPatched.psz);
    1305 
    1306                     xstrcpy(&strParamsPatched, "/3 ", 0);
    1307 
    1308                     if (psz)
    1309                     {
    1310                         xstrcat(&strParamsPatched, psz, 0);
    1311                         free(psz);
    1312                     }
     1312                    xstrcat(&strParamsPatched, psz, 0);
     1313                    free(psz);
    13131314                }
    13141315            }
    1315             else
    1316                 // for all other executable types
    1317                 // (including OS/2 and DOS sessions),
    1318                 // set pszExecutable to NULL; this will
    1319                 // have WinStartApp start a cmd shell
    1320                 Details.pszExecutable = NULL;
    1321 
    1322         } // end if (strcmp(pProgDetails->pszExecutable, "*") == 0)
    1323 
    1324         // else
    1325 
    1326         // no, this else breaks the WINOS2.COM hack above... we
    1327         // need to look for that on the PATH as well
    1328         // V0.9.20 (2002-07-03) [umoeller]
    1329         if (Details.pszExecutable)
    1330         {
    1331             // check the executable and look for it on the
    1332             // PATH if necessary
    1333             if (!(arc = CheckAndQualifyExecutable(&Details,
    1334                                                   &strExecutablePatched)))
     1316        }
     1317        else
     1318            // for all other executable types
     1319            // (including OS/2 and DOS sessions),
     1320            // set pszExecutable to NULL; this will
     1321            // have WinStartApp start a cmd shell
     1322            Details.pszExecutable = NULL;
     1323
     1324    } // end if (strcmp(pProgDetails->pszExecutable, "*") == 0)
     1325
     1326    // else
     1327
     1328    // no, this else breaks the WINOS2.COM hack above... we
     1329    // need to look for that on the PATH as well
     1330    // V0.9.20 (2002-07-03) [umoeller]
     1331    if (Details.pszExecutable)
     1332    {
     1333        // check the executable and look for it on the
     1334        // PATH if necessary
     1335        if (!(arc = CheckAndQualifyExecutable(&Details,
     1336                                              &strExecutablePatched)))
     1337        {
     1338            PSZ pszExtension;
     1339
     1340            // make sure startup dir is really a directory
     1341            // V0.9.20 (2002-07-03) [umoeller]: moved this down
     1342            if (Details.pszStartupDir)
    13351343            {
    1336                 PSZ pszExtension;
    1337 
    1338                 // make sure startup dir is really a directory
    1339                 // V0.9.20 (2002-07-03) [umoeller]: moved this down
    1340                 if (Details.pszStartupDir)
    1341                 {
    1342                     ULONG ulAttr;
    1343                     // it is valid to specify a startup dir of "C:"
    1344                     if (    (strlen(Details.pszStartupDir) > 2)
    1345                          && (!(arc = doshQueryPathAttr(Details.pszStartupDir,
    1346                                                        &ulAttr)))
    1347                          && (!(ulAttr & FILE_DIRECTORY))
    1348                        )
    1349                         arc = ERROR_PATH_NOT_FOUND;
    1350                 }
     1344                ULONG ulAttr;
     1345                // it is valid to specify a startup dir of "C:"
     1346                if (    (strlen(Details.pszStartupDir) > 2)
     1347                     && (!(arc = doshQueryPathAttr(Details.pszStartupDir,
     1348                                                   &ulAttr)))
     1349                     && (!(ulAttr & FILE_DIRECTORY))
     1350                   )
     1351                    arc = ERROR_PATH_NOT_FOUND;
     1352            }
    13511353
    13521354// V0.9.21: this define is never set. I have thus completely
     
    13611363#ifdef ENABLEBATCHHACKS
    13621364
    1363                 // we frequently get here for BAT and CMD files
    1364                 // with progtype == PROG_DEFAULT, so include
    1365                 // that in the check, or all BAT files will fail
    1366                 // V0.9.20 (2002-07-03) [umoeller]
    1367 
    1368                 switch (Details.progt.progc)
     1365            // we frequently get here for BAT and CMD files
     1366            // with progtype == PROG_DEFAULT, so include
     1367            // that in the check, or all BAT files will fail
     1368            // V0.9.20 (2002-07-03) [umoeller]
     1369
     1370            switch (Details.progt.progc)
     1371            {
     1372                /*
     1373                 *  .CMD files fixups
     1374                 *
     1375                 */
     1376
     1377                case PROG_DEFAULT:          // V0.9.20 (2002-07-03) [umoeller]
     1378                case PROG_FULLSCREEN:       // OS/2 fullscreen
     1379                case PROG_WINDOWABLEVIO:    // OS/2 window
    13691380                {
    1370                     /*
    1371                      *  .CMD files fixups
    1372                      *
    1373                      */
    1374 
    1375                     case PROG_DEFAULT:          // V0.9.20 (2002-07-03) [umoeller]
    1376                     case PROG_FULLSCREEN:       // OS/2 fullscreen
    1377                     case PROG_WINDOWABLEVIO:    // OS/2 window
     1381                    if (    (pszExtension = doshGetExtension(Details.pszExecutable))
     1382                         && (!stricmp(pszExtension, "CMD"))
     1383                       )
    13781384                    {
    1379                         if (    (pszExtension = doshGetExtension(Details.pszExecutable))
    1380                              && (!stricmp(pszExtension, "CMD"))
    1381                            )
    1382                         {
    1383                             arc = CallBatchCorrectly(&Details,
    1384                                                      &strExecutablePatched,
    1385                                                      &strParamsPatched,
    1386                                                      "OS2_SHELL",
    1387                                                      "CMD.EXE");
    1388                         }
     1385                        arc = CallBatchCorrectly(&Details,
     1386                                                 &strExecutablePatched,
     1387                                                 &strParamsPatched,
     1388                                                 "OS2_SHELL",
     1389                                                 "CMD.EXE");
    13891390                    }
    1390                     break;
    13911391                }
    1392 
    1393                 switch (Details.progt.progc)
     1392                break;
     1393            }
     1394
     1395            switch (Details.progt.progc)
     1396            {
     1397                case PROG_DEFAULT:          // V0.9.20 (2002-07-03) [umoeller]
     1398                case PROG_VDM:              // DOS fullscreen
     1399                case PROG_WINDOWEDVDM:      // DOS window
    13941400                {
    1395                     case PROG_DEFAULT:          // V0.9.20 (2002-07-03) [umoeller]
    1396                     case PROG_VDM:              // DOS fullscreen
    1397                     case PROG_WINDOWEDVDM:      // DOS window
     1401                    if (    (pszExtension = doshGetExtension(Details.pszExecutable))
     1402                         && (!stricmp(pszExtension, "BAT"))
     1403                       )
    13981404                    {
    1399                         if (    (pszExtension = doshGetExtension(Details.pszExecutable))
    1400                              && (!stricmp(pszExtension, "BAT"))
    1401                            )
    1402                         {
    1403                             arc = CallBatchCorrectly(&Details,
    1404                                                      &strExecutablePatched,
    1405                                                      &strParamsPatched,
    1406                                                      // there is no environment variable
    1407                                                      // for the DOS shell
    1408                                                      NULL,
    1409                                                      "COMMAND.COM");
    1410                         }
     1405                        arc = CallBatchCorrectly(&Details,
     1406                                                 &strExecutablePatched,
     1407                                                 &strParamsPatched,
     1408                                                 // there is no environment variable
     1409                                                 // for the DOS shell
     1410                                                 NULL,
     1411                                                 "COMMAND.COM");
    14111412                    }
    1412                     break;
    1413                 } // end switch (Details.progt.progc)
     1413                }
     1414                break;
     1415            } // end switch (Details.progt.progc)
    14141416#endif // ENABLEBATCHHACKS
     1417        }
     1418    }
     1419
     1420    if (!arc)
     1421    {
     1422        if (    (ulIsWinApp)
     1423             && (    (!(Details.pszEnvironment))
     1424                  || (!(*Details.pszEnvironment))
     1425                )
     1426           )
     1427        {
     1428            // this is a windoze app, and caller didn't bother
     1429            // to give us an environment:
     1430            // we MUST set one then, or we'll get the strangest
     1431            // errors, up to system hangs. V0.9.12 (2001-05-26) [umoeller]
     1432
     1433            DOSENVIRONMENT Env = {0};
     1434
     1435            // get standard WIN-OS/2 environment
     1436            PSZ pszTemp;
     1437            if (!(arc = appQueryDefaultWin31Environment(&pszTemp)))
     1438            {
     1439                if (!(arc = appParseEnvironment(pszTemp,
     1440                                                &Env)))
     1441                {
     1442                    // now override KBD_CTRL_BYPASS=CTRL_ESC
     1443                    if (    (!(arc = appSetEnvironmentVar(&Env,
     1444                                                          "KBD_CTRL_BYPASS=CTRL_ESC",
     1445                                                          FALSE)))        // add last
     1446                         && (!(arc = appConvertEnvironment(&Env,
     1447                                                           &pszWinOS2Env,   // freed at bottom
     1448                                                           NULL)))
     1449                       )
     1450                        Details.pszEnvironment = pszWinOS2Env;
     1451
     1452                    appFreeEnvironment(&Env);
     1453                }
     1454
     1455                free(pszTemp);
    14151456            }
    14161457        }
     
    14181459        if (!arc)
    14191460        {
    1420             if (    (ulIsWinApp)
    1421                  && (    (!(Details.pszEnvironment))
    1422                       || (!(*Details.pszEnvironment))
    1423                     )
    1424                )
     1461            // if no title is given, use the executable
     1462            if (!Details.pszTitle)
     1463                Details.pszTitle = Details.pszExecutable;
     1464
     1465            // make sure params have a leading space
     1466            // V0.9.18 (2002-03-27) [umoeller]
     1467            if (strParamsPatched.ulLength)
    14251468            {
    1426                 // this is a windoze app, and caller didn't bother
    1427                 // to give us an environment:
    1428                 // we MUST set one then, or we'll get the strangest
    1429                 // errors, up to system hangs. V0.9.12 (2001-05-26) [umoeller]
    1430 
    1431                 DOSENVIRONMENT Env = {0};
    1432 
    1433                 // get standard WIN-OS/2 environment
    1434                 PSZ pszTemp;
    1435                 if (!(arc = appQueryDefaultWin31Environment(&pszTemp)))
     1469                if (strParamsPatched.psz[0] != ' ')
    14361470                {
    1437                     if (!(arc = appParseEnvironment(pszTemp,
    1438                                                     &Env)))
    1439                     {
    1440                         // now override KBD_CTRL_BYPASS=CTRL_ESC
    1441                         if (    (!(arc = appSetEnvironmentVar(&Env,
    1442                                                               "KBD_CTRL_BYPASS=CTRL_ESC",
    1443                                                               FALSE)))        // add last
    1444                              && (!(arc = appConvertEnvironment(&Env,
    1445                                                                &pszWinOS2Env,   // freed at bottom
    1446                                                                NULL)))
    1447                            )
    1448                             Details.pszEnvironment = pszWinOS2Env;
    1449 
    1450                         appFreeEnvironment(&Env);
    1451                     }
    1452 
    1453                     free(pszTemp);
     1471                    XSTRING str2;
     1472                    xstrInit(&str2, 0);
     1473                    xstrcpy(&str2, " ", 1);
     1474                    xstrcats(&str2, &strParamsPatched);
     1475                    xstrcpys(&strParamsPatched, &str2);
     1476                    xstrClear(&str2);
     1477                            // we really need xstrInsert or something
    14541478                }
     1479                Details.pszParameters = strParamsPatched.psz;
    14551480            }
    1456 
    1457             if (!arc)
    1458             {
    1459                 // if no title is given, use the executable
    1460                 if (!Details.pszTitle)
    1461                     Details.pszTitle = Details.pszExecutable;
    1462 
    1463                 // make sure params have a leading space
    1464                 // V0.9.18 (2002-03-27) [umoeller]
    1465                 if (strParamsPatched.ulLength)
    1466                 {
    1467                     if (strParamsPatched.psz[0] != ' ')
    1468                     {
    1469                         XSTRING str2;
    1470                         xstrInit(&str2, 0);
    1471                         xstrcpy(&str2, " ", 1);
    1472                         xstrcats(&str2, &strParamsPatched);
    1473                         xstrcpys(&strParamsPatched, &str2);
    1474                         xstrClear(&str2);
    1475                                 // we really need xstrInsert or something
    1476                     }
    1477                     Details.pszParameters = strParamsPatched.psz;
    1478                 }
    1479                 else
    1480                     // never pass null pointers
    1481                     Details.pszParameters = "";
    1482 
     1481            else
    14831482                // never pass null pointers
    1484                 if (!Details.pszIcon)
    1485                     Details.pszIcon = "";
    1486 
    1487                 // never pass null pointers
    1488                 if (!Details.pszStartupDir)
    1489                     Details.pszStartupDir = "";
    1490 
    1491             }
     1483                Details.pszParameters = "";
     1484
     1485            // never pass null pointers
     1486            if (!Details.pszIcon)
     1487                Details.pszIcon = "";
     1488
     1489            // never pass null pointers
     1490            if (!Details.pszStartupDir)
     1491                Details.pszStartupDir = "";
     1492
    14921493        }
    14931494    }
     
    20742075    CHAR           szDir[CCHMAXPATH] = "";
    20752076    PCSZ           p;
    2076     HWND           hwndObject;
     2077    HWND           hwndObject = NULLHANDLE;
    20772078
    20782079    pd.Length = sizeof(pd);
     
    20922093    }
    20932094
    2094     if (    (hwndObject = winhCreateObjectWindow(WC_STATIC, NULL))
     2095    if (pulExitCode)
     2096        if (!(hwndObject = winhCreateObjectWindow(WC_STATIC, NULL)))
     2097            arc = ERROR_NOT_ENOUGH_MEMORY;
     2098
     2099    if (    (!arc)
    20952100         && (!(arc = appStartApp(hwndObject,
    20962101                                 &pd,
     
    21052110                          *phapp,
    21062111                          pulExitCode);
    2107 
     2112    }
     2113
     2114    if (hwndObject)
    21082115        WinDestroyWindow(hwndObject);       // was missing V0.9.20 (2002-08-10) [umoeller]
    2109     }
    21102116
    21112117    return arc;
     
    21212127 *
    21222128 *@@added V0.9.20 (2002-08-10) [umoeller]
     2129 *@@changed V0.9.21 (2002-08-21) [umoeller]: changed prototype to return browser
    21232130 */
    21242131
    21252132APIRET appOpenURL(PCSZ pcszURL,           // in: URL to open
    2126                   PSZ pszAppStarted,      // out: application that was started
     2133                  PSZ pszAppStarted,      // out: application that was started (req.)
    21272134                  ULONG cbAppStarted)     // in: size of that buffer
    21282135{
    2129     APIRET      arc = NO_ERROR;
    2130 
    2131     CHAR        szBrowser[CCHMAXPATH],
    2132                 szStartupDir[CCHMAXPATH];
     2136    APIRET      arc = ERROR_NO_DATA;
     2137
     2138    CHAR        szStartupDir[CCHMAXPATH];
    21332139    XSTRING     strParameters;
     2140
     2141    if (    (!pcszURL)
     2142         || (!pszAppStarted)
     2143         || (!cbAppStarted)
     2144       )
     2145        return ERROR_INVALID_PARAMETER;
    21342146
    21352147    xstrInit(&strParameters, 0);
     
    21392151                              "DefaultBrowserExe",
    21402152                              "NETSCAPE.EXE",
    2141                               szBrowser,
    2142                               sizeof(szBrowser)))
     2153                              pszAppStarted,
     2154                              cbAppStarted))
    21432155    {
    21442156        PSZ     pszDefParams;
     
    21642176                              sizeof(szStartupDir));
    21652177
    2166 
    2167         arc = appQuickStartApp(szBrowser,
     2178        arc = appQuickStartApp(pszAppStarted,
    21682179                               PROG_DEFAULT,
    21692180                               strParameters.psz,
     
    21712182                               &happ,
    21722183                               NULL);     // don't wait
    2173 
    2174         if (pszAppStarted)
    2175             strhncpy0(pszAppStarted,
    2176                       szBrowser,
    2177                       cbAppStarted);
    21782184    }
    21792185
  • trunk/src/helpers/debug.c

    r142 r212  
    1 
    21/*
    32 *@@sourcefile debug.c:
     
    4443 *      GNU General Public License for more details.
    4544 */
     45
     46//#define DEBUG_SYMDUMP // enable to dump sym file to log V0.9.21 (2002-08-21) [paperino]
    4647
    4748#define OS2EMX_PLAIN_CHAR
     
    15361537
    15371538    // open .SYM file
     1539#ifdef DEBUG_SYMDUMP    // V0.9.21 (2002-08-21) [paperino]
     1540    fprintf(LogFile,"Dump of '%s' for object %d\n",SymFileName,Object);
     1541#endif
    15381542    SymFile = fopen(SymFileName, "rb");
    15391543    if (SymFile == 0)
     
    15421546    // read in first map definition
    15431547    fread(&MapDef, sizeof(MAPDEF), 1, SymFile);
     1548#ifdef DEBUG_SYMDUMP
     1549    Buffer[0] = MapDef.achModName[0];
     1550    fread(&Buffer[1], 1, MapDef.cbModName-1, SymFile);
     1551    Buffer[MapDef.cbModName] = 0x00;
     1552    fprintf(LogFile,"Module name '%s'\n",Buffer);
     1553#endif
    15441554
    15451555    SegOffset = SEGDEFOFFSET(MapDef);
     1556#ifdef DEBUG_SYMDUMP
     1557    fprintf(LogFile,"SegOffset %0x\n",SegOffset);
     1558#endif
    15461559
    15471560    // go thru all segments
     
    15501563         SegNum++)
    15511564    {
    1552         // printf("Scanning segment #%d Offset %4.4hX\n",SegNum+1,SegOffset);
     1565#ifdef DEBUG_SYMDUMP
     1566        fprintf(LogFile,"Scanning segment #%d Offset %08X\n",SegNum,SegOffset);
     1567#endif
    15531568        if (fseek(SymFile, SegOffset, SEEK_SET))
    15541569            // seek error
     
    15571572        // read in segment definition
    15581573        fread(&SegDef, sizeof(SEGDEF), 1, SymFile);
     1574        Buffer[0] = SegDef.achSegName[0];
     1575        fread(&Buffer[1], 1, SegDef.cbSegName-1, SymFile);
     1576        Buffer[SegDef.cbSegName] = 0x00;
     1577#ifdef DEBUG_SYMDUMP
     1578        fprintf(LogFile,"Segment name '%s', number %d, flags %02x\n",Buffer,SegNum,SegDef.bFlags);
     1579#endif
     1580
    15591581        if (SegNum == Object)
    15601582        {
     
    15641586
    15651587            // go thru all symbols in this object
     1588#ifdef DEBUG_SYMDUMP
     1589            fprintf(LogFile,"Scanning #%d symbols\n",SegDef.cSymbols);
     1590#endif
    15661591            for (SymNum = 0; SymNum < SegDef.cSymbols; SymNum++)
    15671592            {
    1568 
    1569                 // read in symbol offset USHORT
    1570                 SymPtrOffset = SYMDEFOFFSET(SegOffset, SegDef, SymNum);
    1571                 fseek(SymFile, SymPtrOffset, SEEK_SET);
    1572                 fread(&SymOffset, sizeof(unsigned short int), 1, SymFile);
    1573 
    1574                 // go to symbol definition
    1575                 fseek(SymFile, SymOffset + SegOffset, SEEK_SET);
    1576 
     1593                // fixed syms > 64 K V0.9.21 (2002-08-21) [paperino]
    15771594                if (SegDef.bFlags & 0x01)
    15781595                {
     
    15941611                    LastVal = SymDef32.wSymVal;
    15951612                    Buffer[0] = SymDef32.achSymName[0];
    1596                     fread(&Buffer[1], 1, SymDef32.cbSymName, SymFile);
     1613                    fread(&Buffer[1], 1, SymDef32.cbSymName-1, SymFile);
    15971614                    Buffer[SymDef32.cbSymName] = 0x00;
     1615#ifdef DEBUG_SYMDUMP
     1616                    fprintf(LogFile,"32 Bit Symbol Address %08p <%s> \n",SymDef32.wSymVal,Buffer);
     1617#endif
    15981618
    15991619                    if (SymDef32.wSymVal > TrapOffset)
     
    16081628                        break;
    16091629                    }
    1610                     /*printf("32 Bit Symbol <%s> Address %p\n",Buffer,SymDef32.wSymVal); */
    16111630                }
    16121631                else
     
    16231642                    LastVal = SymDef16.wSymVal;
    16241643                    Buffer[0] = SymDef16.achSymName[0];
    1625                     fread(&Buffer[1], 1, SymDef16.cbSymName, SymFile);
     1644                    fread(&Buffer[1], 1, SymDef16.cbSymName-1, SymFile);
    16261645                    Buffer[SymDef16.cbSymName] = 0x00;
    16271646                    if (SymDef16.wSymVal > TrapOffset)
     
    16341653                        break;
    16351654                    }
    1636                     /*printf("16 Bit Symbol <%s> Address %p\n",Buffer,SymDef16.wSymVal); */
     1655#ifdef DEBUG_SYMDUMP
     1656                    fprintf(LogFile,"16 Bit Symbol <%s> Address %p\n",Buffer,SymDef16.wSymVal);
     1657#endif
    16371658                }               // endif
    16381659            }
  • trunk/src/helpers/encodings.c

    r209 r212  
    6767 *      number and a descriptive string.
    6868 *
     69 *      For a way too extensive list of codepage
     70 *      names, see "http://www.iana.org/assignments/character-sets".
     71 *
    6972 *@@added V [umoeller]
    7073 */
     
    7679    unsigned long       cEntries;           // entries in map (array item count)
    7780    unsigned short      usCodepageOS2;      // corresponding OS/2 codepage or 0 if none
     81                                            // V0.9.21 (2002-08-21) [umoeller]
     82    unsigned short      usLatin;            // ISO 8859-X correspondance or 0
    7883    ENCBYTECOUNT        bc;
    7984    const char          *pcszDescription;   // description
     
    8287        #define ENCODINGENTRY(id)   enc_ ## id, G_ ## id, ARRAYITEMCOUNT(G_ ## id)
    8388
    84         ENCODINGENTRY(cp437), 437, SINGLE, "DOS Latin US",
    85         ENCODINGENTRY(cp737), 737, SINGLE, "DOS Greek",
    86         ENCODINGENTRY(cp775), 775, SINGLE, "DOS BaltRim",
    87         ENCODINGENTRY(cp850), 850, SINGLE, "DOS Latin 1",
    88         ENCODINGENTRY(cp852), 852, SINGLE, "DOS Latin 2",               // default in Hungary,
    89                                                                 // Romania, Poland
    90         ENCODINGENTRY(cp855), 855, SINGLE, "DOS Cyrillic",
    91         ENCODINGENTRY(cp857), 857, SINGLE, "DOS Latin 5 (Turkish)",
    92         ENCODINGENTRY(cp860), 860, SINGLE, "DOS Portuguese",
    93         ENCODINGENTRY(cp861), 861, SINGLE, "DOS Icelandic",
    94         ENCODINGENTRY(cp862), 862, SINGLE, "DOS Hebrew",
    95         ENCODINGENTRY(cp863), 863, SINGLE, "DOS Canadian French",
    96         ENCODINGENTRY(cp864), 864, SINGLE, "DOS Arabic",                // default in Egypt
    97         ENCODINGENTRY(cp865), 865, SINGLE, "DOS Nordic",
    98         ENCODINGENTRY(cp866), 866, SINGLE, "DOS Cyrillic Russian",      // default in Russia
    99         ENCODINGENTRY(cp869), 869, SINGLE, "DOS Greek2",
    100         ENCODINGENTRY(cp874), 874, SINGLE, "DOS Thai (TIS-620)",        // default in Thailand
    101 
    102         ENCODINGENTRY(cp932), 932 /* or 943?*/ , DOUBLE, "Japanese Windows",
    103         ENCODINGENTRY(cp936), 936 /* or 946?*/ , DOUBLE, "Chinese",
    104         ENCODINGENTRY(cp949), 951 /* or 949?*/ , DOUBLE, "Korean",
    105         ENCODINGENTRY(cp950), 947 /* or 950?*/ , DOUBLE, "Taiwan Big-5",           // default in China?
    106 
    107         ENCODINGENTRY(cp1004), 1004, SINGLE, "Windows Extended",
    108         ENCODINGENTRY(cp1250), 1250, SINGLE, "Windows Latin 2",
    109         ENCODINGENTRY(cp1251), 1251, SINGLE, "Windows Cyrillic",
    110         ENCODINGENTRY(cp1252), 1252, SINGLE, "Windows Latin 1",
    111         ENCODINGENTRY(cp1253), 1253, SINGLE, "Windows Greek",
    112         ENCODINGENTRY(cp1254), 1254, SINGLE, "Windows Turkish",
    113         ENCODINGENTRY(cp1255), 1255, SINGLE, "Windows Hebrew",
    114         ENCODINGENTRY(cp1256), 1256, SINGLE, "Windows Arabic",
    115         ENCODINGENTRY(cp1257), 1257, SINGLE, "Windows Latin-4",
    116         ENCODINGENTRY(cp1258), 1258, UNKNOWN, "unknown",
    117         ENCODINGENTRY(iso8859_1), 819, SINGLE, "ISO/IEC 8859-1:1998 (Latin-1)",
    118         ENCODINGENTRY(iso8859_2), 912, SINGLE, "ISO 8859-2:1999 (Latin-2)",
    119         ENCODINGENTRY(iso8859_3), 913, SINGLE, "ISO/IEC 8859-3:1999 (Latin-3)",
    120         ENCODINGENTRY(iso8859_4), 914, SINGLE, "ISO/IEC 8859-4:1998 (Latin-4)",
    121         ENCODINGENTRY(iso8859_5), 915, SINGLE, "ISO 8859-5:1999 (Cyrillic)",
    122         ENCODINGENTRY(iso8859_6), 1089, SINGLE, "ISO 8859-6:1999 (Arabic)",
    123         ENCODINGENTRY(iso8859_7), 813, SINGLE, "ISO 8859-7:1987 (Greek)",   // default in Greece
    124         ENCODINGENTRY(iso8859_8), 916, SINGLE, "ISO/IEC 8859-8:1999 (Hebrew)",
    125         ENCODINGENTRY(iso8859_9), 920, SINGLE, "ISO/IEC 8859-9:1999 (Latin-5)",
    126         ENCODINGENTRY(iso8859_10), 0, SINGLE, "ISO/IEC 8859-10:1998",
    127         ENCODINGENTRY(iso8859_13), 0, SINGLE, "ISO/IEC 8859-13:1998",
    128         ENCODINGENTRY(iso8859_14), 0, SINGLE, "ISO/IEC 8859-14:1998",
    129         ENCODINGENTRY(iso8859_15), 923, SINGLE, "ISO/IEC 8859-15:1999",
    130 
    131         UNSUPPORTED, NULL, 0, 1200, MULTI_UNICODE, "Unicode UCS-2",
    132         UNSUPPORTED, NULL, 0, 1208, MULTI_UNICODE, "Unicode UTF-8"
     89        ENCODINGENTRY(cp437),      437,  0, SINGLE, "DOS Latin US",
     90        ENCODINGENTRY(cp737),      737,  0, SINGLE, "DOS Greek",
     91        ENCODINGENTRY(cp775),      775,  0, SINGLE, "DOS BaltRim",
     92        ENCODINGENTRY(cp850),      850,  0, SINGLE, "DOS Latin 1",
     93        ENCODINGENTRY(cp852),      852,  0, SINGLE, "DOS Latin 2",               // default in Hungary,
     94                                                                                 // Romania, Poland
     95        ENCODINGENTRY(cp855),      855,  0, SINGLE, "DOS Cyrillic",
     96        ENCODINGENTRY(cp857),      857,  0, SINGLE, "DOS Latin 5 (Turkish)",
     97        ENCODINGENTRY(cp860),      860,  0, SINGLE, "DOS Portuguese",
     98        ENCODINGENTRY(cp861),      861,  0, SINGLE, "DOS Icelandic",
     99        ENCODINGENTRY(cp862),      862,  0, SINGLE, "DOS Hebrew",
     100        ENCODINGENTRY(cp863),      863,  0, SINGLE, "DOS Canadian French",
     101        ENCODINGENTRY(cp864),      864,  0, SINGLE, "DOS Arabic",                // default in Egypt
     102        ENCODINGENTRY(cp865),      865,  0, SINGLE, "DOS Nordic",
     103        ENCODINGENTRY(cp866),      866,  0, SINGLE, "DOS Cyrillic Russian",      // default in Russia
     104        ENCODINGENTRY(cp869),      869,  0, SINGLE, "DOS Greek2",
     105        ENCODINGENTRY(cp874),      874,  0, SINGLE, "DOS Thai (TIS-620)",        // default in Thailand
     106
     107        ENCODINGENTRY(cp932),      932 /* or 943?*/ ,
     108                                         0, DOUBLE, "Japanese Windows",
     109        ENCODINGENTRY(cp936),      936 /* or 946?*/ ,
     110                                         0, DOUBLE, "Chinese",
     111        ENCODINGENTRY(cp949),      951 /* or 949?*/ ,
     112                                         0, DOUBLE, "Korean",
     113        ENCODINGENTRY(cp950),      947 /* or 950?*/ ,
     114                                         0, DOUBLE, "Taiwan Big-5",           // default in China?
     115
     116        ENCODINGENTRY(cp1004),    1004,  0, SINGLE, "Windows Extended",
     117        ENCODINGENTRY(cp1250),    1250,  0, SINGLE, "Windows Latin 2",
     118        ENCODINGENTRY(cp1251),    1251,  0, SINGLE, "Windows Cyrillic",
     119        ENCODINGENTRY(cp1252),    1252,  0, SINGLE, "Windows Latin 1",
     120        ENCODINGENTRY(cp1253),    1253,  0, SINGLE, "Windows Greek",
     121        ENCODINGENTRY(cp1254),    1254,  0, SINGLE, "Windows Turkish",
     122        ENCODINGENTRY(cp1255),    1255,  0, SINGLE, "Windows Hebrew",
     123        ENCODINGENTRY(cp1256),    1256,  0, SINGLE, "Windows Arabic",
     124        ENCODINGENTRY(cp1257),    1257,  0, SINGLE, "Windows Latin-4",
     125        ENCODINGENTRY(cp1258),    1258,  0, UNKNOWN, "unknown",
     126        ENCODINGENTRY(iso8859_1),  819,  1, SINGLE, "ISO/IEC 8859-1:1998 (Latin-1)",
     127        ENCODINGENTRY(iso8859_2),  912,  2, SINGLE, "ISO 8859-2:1999 (Latin-2)",
     128        ENCODINGENTRY(iso8859_3),  913,  3, SINGLE, "ISO/IEC 8859-3:1999 (Latin-3)",
     129        ENCODINGENTRY(iso8859_4),  914,  4, SINGLE, "ISO/IEC 8859-4:1998 (Latin-4)",
     130        ENCODINGENTRY(iso8859_5),  915,  5, SINGLE, "ISO 8859-5:1999 (Cyrillic)",
     131        ENCODINGENTRY(iso8859_6), 1089,  6, SINGLE, "ISO 8859-6:1999 (Arabic)",
     132        ENCODINGENTRY(iso8859_7),  813,  7, SINGLE, "ISO 8859-7:1987 (Greek)",   // default in Greece
     133        ENCODINGENTRY(iso8859_8),  916,  8, SINGLE, "ISO/IEC 8859-8:1999 (Hebrew)",
     134        ENCODINGENTRY(iso8859_9),  920,  9, SINGLE, "ISO/IEC 8859-9:1999 (Latin-5)",
     135        ENCODINGENTRY(iso8859_10),   0, 10, SINGLE, "ISO/IEC 8859-10:1998",
     136        ENCODINGENTRY(iso8859_13),   0, 13, SINGLE, "ISO/IEC 8859-13:1998",
     137        ENCODINGENTRY(iso8859_14),   0, 14, SINGLE, "ISO/IEC 8859-14:1998",
     138        ENCODINGENTRY(iso8859_15), 923, 15, SINGLE, "ISO/IEC 8859-15:1999",
     139
     140        UNSUPPORTED, NULL, 0,     1200,  0, MULTI_UNICODE, "Unicode UCS-2",
     141        UNSUPPORTED, NULL, 0,     1208,  0, MULTI_UNICODE, "Unicode UTF-8"
    133142    };
    134143
  • trunk/src/helpers/xml.c

    r196 r212  
    31013101
    31023102/*
     3103 *@@ ESCAPES:
     3104 *
     3105 *@@added V0.9.21 (2002-08-21) [umoeller]
     3106 */
     3107
     3108typedef struct _ESCAPES
     3109{
     3110    XSTRING strQuot1,       // "
     3111            strQuot2,       // &quot;
     3112            strAmp1,        // &
     3113            strAmp2,        // &amp;
     3114            strLT1,         // <
     3115            strLT2,         // &lt;
     3116            strGT1,         // >
     3117            strGT2;         // &gt;
     3118
     3119    XSTRING strTemp;        // temp buffer
     3120
     3121} ESCAPES, *PESCAPES;
     3122
     3123/*
     3124 *@@ DoEscapes:
     3125 *
     3126 *@@added V0.9.21 (2002-08-21) [umoeller]
     3127 */
     3128
     3129VOID DoEscapes(PESCAPES pEscapes,
     3130               BOOL fQuotesToo)
     3131{
     3132    ULONG   ulOfs;
     3133    size_t  ShiftTable[256];
     3134    BOOL    fRepeat;
     3135
     3136    if (fQuotesToo)
     3137    {
     3138        ulOfs = 0;
     3139        fRepeat = FALSE;
     3140        while (xstrFindReplace(&pEscapes->strTemp,
     3141                               &ulOfs,
     3142                               &pEscapes->strQuot1,
     3143                               &pEscapes->strQuot2,
     3144                               ShiftTable,
     3145                               &fRepeat))
     3146            ;
     3147    }
     3148
     3149    ulOfs = 0;
     3150    fRepeat = FALSE;
     3151    while (xstrFindReplace(&pEscapes->strTemp,
     3152                           &ulOfs,
     3153                           &pEscapes->strLT1,
     3154                           &pEscapes->strLT2,
     3155                           ShiftTable,
     3156                           &fRepeat))
     3157        ;
     3158
     3159    ulOfs = 0;
     3160    fRepeat = FALSE;
     3161    while (xstrFindReplace(&pEscapes->strTemp,
     3162                           &ulOfs,
     3163                           &pEscapes->strGT1,
     3164                           &pEscapes->strGT2,
     3165                           ShiftTable,
     3166                           &fRepeat))
     3167        ;
     3168
     3169    // replace ampersands last
     3170    ulOfs = 0;
     3171    fRepeat = FALSE;
     3172    while (xstrFindReplace(&pEscapes->strTemp,
     3173                           &ulOfs,
     3174                           &pEscapes->strAmp1,
     3175                           &pEscapes->strAmp2,
     3176                           ShiftTable,
     3177                           &fRepeat))
     3178        ;
     3179}
     3180
     3181/*
    31033182 *@@ WriteNodes:
    31043183 *      internal helper for writing out the nodes.
     
    31063185 *
    31073186 *@@added V0.9.12 (2001-05-21) [umoeller]
     3187 *@@changed V0.9.21 (2002-08-21) [umoeller]: changed prototype, fixed unescaped characters in attributes and content
    31083188 */
    31093189
    31103190static VOID WriteNodes(PXSTRING pxstr,
     3191                       PESCAPES pEscapes,
    31113192                       PDOMNODE pDomNode)       // in: node whose children are to be written (initially DOCUMENT)
    31123193{
     
    31433224                    xstrcats(pxstr, &pAttribNode->NodeBase.strNodeName);
    31443225                    xstrcat(pxstr, "=\"", 0);
    3145                     xstrcats(pxstr, pAttribNode->pstrNodeValue);
     3226
     3227                    // copy attribute value to temp buffer first
     3228                    // so we can escape quotes and ampersands
     3229                    // V0.9.21 (2002-08-21) [umoeller]
     3230                    xstrcpys(&pEscapes->strTemp, pAttribNode->pstrNodeValue);
     3231
     3232                    DoEscapes(pEscapes,
     3233                              TRUE);        // quotes too
     3234
     3235                    // alright, use that
     3236                    xstrcats(pxstr, &pEscapes->strTemp);
    31463237                    xstrcatc(pxstr, '\"');
    31473238                }
     
    31543245
    31553246                    // recurse into this child element
    3156                     WriteNodes(pxstr, pChildNode);
     3247                    WriteNodes(pxstr, pEscapes, pChildNode);
    31573248
    31583249                    if (!fMixedContent)
     
    31763267            case DOMNODE_COMMENT:
    31773268                // that's simple
    3178                 xstrcats(pxstr, pChildNode->pstrNodeValue);
     3269                xstrcpys(&pEscapes->strTemp,
     3270                         pChildNode->pstrNodeValue);
     3271
     3272                DoEscapes(pEscapes,         // V0.9.21 (2002-08-21) [umoeller]
     3273                          FALSE);           // quotes not
     3274
     3275                xstrcats(pxstr, &pEscapes->strTemp);
    31793276            break;
    31803277
     
    32883385    else
    32893386    {
     3387        ESCAPES esc;
     3388
    32903389        // <?xml version="1.0" encoding="ISO-8859-1"?>
    32913390        xstrcpy(pxstr, "<?xml version=\"1.0\" encoding=\"", 0);
     
    33013400        }
    33023401
     3402        xstrInitCopy(&esc.strQuot1, "\"", 0);
     3403        xstrInitCopy(&esc.strQuot2, "&quot;", 0);
     3404        xstrInitCopy(&esc.strAmp1, "&", 0);
     3405        xstrInitCopy(&esc.strAmp2, "&amp;", 0);
     3406        xstrInitCopy(&esc.strLT1, "<", 0);
     3407        xstrInitCopy(&esc.strLT2, "&lt;", 0);
     3408        xstrInitCopy(&esc.strGT1, ">", 0);
     3409        xstrInitCopy(&esc.strGT2, "&gt;", 0);
     3410
     3411        xstrInit(&esc.strTemp, 0);       // temp buffer
     3412
    33033413        // write out children
    3304         WriteNodes(pxstr, (PDOMNODE)pDocument);
     3414        WriteNodes(pxstr, &esc, (PDOMNODE)pDocument);
     3415
     3416        xstrClear(&esc.strQuot1);
     3417        xstrClear(&esc.strQuot2);
     3418        xstrClear(&esc.strAmp1);
     3419        xstrClear(&esc.strAmp2);
     3420        xstrClear(&esc.strLT1);
     3421        xstrClear(&esc.strLT2);
     3422        xstrClear(&esc.strGT1);
     3423        xstrClear(&esc.strGT2);
     3424
     3425        xstrClear(&esc.strTemp);       // temp buffer
    33053426
    33063427        xstrcatc(pxstr, '\n');
Note: See TracChangeset for help on using the changeset viewer.