pre-inst/pre-inst.c
author J. Ali Harlow <ali@juiblex.co.uk>
Mon Aug 31 07:12:16 2020 +0100 (2020-08-31)
changeset 104 5cb36c12ac49
parent 97 55ae076f393c
child 109 2947214c450e
permissions -rw-r--r--
Prepare to release 0.6
     1 /*
     2  * Copyright (C) 2014, 2020  J. Ali Harlow <ali@juiblex.co.uk>
     3  *
     4  * This program is free software; you can redistribute it and/or modify
     5  * it under the terms of the GNU General Public License as published by
     6  * the Free Software Foundation; either version 2 of the License, or
     7  * (at your option) any later version.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License along
    15  * with this program; if not, write to the Free Software Foundation, Inc.,
    16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    17  *
    18  * References:
    19  *	http://www.transmissionzero.co.uk/computing/win32-apps-with-mingw/
    20  */
    21 
    22 #include "config.h"
    23 #ifndef WIN32
    24 #define _XOPEN_SOURCE 500
    25 #endif
    26 #include <stdlib.h>
    27 #include <stdio.h>
    28 #include <string.h>
    29 #include <errno.h>
    30 #include <lua.h>
    31 #include <razor.h>
    32 #include <plover/plover.h>
    33 #include <plover/uri-handler.h>
    34 #include <whelk/whelk.h>
    35 #ifdef WIN32
    36 #include <windows.h>
    37 #include <process.h>
    38 #include <commctrl.h>
    39 #include "resource.h"
    40 
    41 #ifndef FOF_NO_UI
    42 #define FOF_NO_UI	(FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI|\
    43 			FOF_NOCONFIRMMKDIR)
    44 #endif
    45 
    46 #else	/* WIN32 */
    47 #include <ftw.h>
    48 #endif	/* WIN32 */
    49 #include "post.h"
    50 
    51 #ifdef WIN32
    52 /* Under WIN32, g_spawn requires a helper program which we'd rather avoid */
    53 #undef USE_G_SPAWN
    54 #else
    55 #define USE_G_SPAWN
    56 #endif
    57 
    58 LUALIB_API int luaopen_posix(lua_State *L);
    59 
    60 #ifdef WIN32
    61 DWORD main_thread_id;
    62 #endif
    63 
    64 gchar *prefix;
    65 
    66 int verify_and_fix(const char *root)
    67 {
    68     return 0;
    69 }
    70 
    71 #ifdef WIN32
    72 INT_PTR CALLBACK ProgressDialogProc(HWND dialog,UINT msg,WPARAM w_param,
    73   LPARAM l_param)
    74 {
    75     HWND progress;
    76     DWORD style;
    77     switch (msg)
    78     {
    79 	case WM_INITDIALOG:
    80 	    progress=GetDlgItem(dialog,IDC_PROGRESS); 
    81 	    style=GetWindowLong(progress,GWL_STYLE);
    82 	    SetWindowLong(progress,GWL_STYLE,style|PBS_MARQUEE);
    83 	    SendMessage(progress,PBM_SETMARQUEE,(WPARAM)TRUE,(LPARAM)30);
    84 	    return (INT_PTR)TRUE;
    85     }
    86     return (INT_PTR)FALSE;
    87 }
    88 #endif
    89 
    90 #ifdef WIN32
    91 __stdcall
    92 #endif
    93 unsigned pre_install_thread(void *data)
    94 {
    95     gboolean changed;
    96     int retval;
    97     const char *repository=data;
    98     gchar *s,*uri;
    99     char *install[]={"plover-gtkui",NULL};
   100     char **pkgs=install;
   101     struct comps *comps;
   102     struct comps_group *group;
   103     struct comps_requirement *pkg;
   104     struct plover_vector *packages=NULL;
   105     GError *error=NULL;
   106     plover__uri_handler_init();
   107     prefix=plover_pre_install_prefix();
   108     s=g_strconcat(prefix,"/var/log/pre-install",NULL);
   109     plover_log_open(s);
   110     g_free(s);
   111     s=g_strconcat(prefix,"/var/lib/razor",NULL);
   112     uri=razor_path_to_uri(s);
   113     g_free(s);
   114     razor_set_database_uri(uri);
   115     free(uri);
   116     if (verify_and_fix(prefix))
   117     {
   118 	g_free(prefix);
   119 	return -1;
   120     }
   121     s=g_strconcat(repository,"/repodata/comps.xml",NULL);
   122     comps=plover_comps_new_from_uri(s,&error);
   123     retval=!comps;
   124     g_free(s);
   125     if (!retval)
   126     {
   127 	group=plover_comps_lookup_group(comps,"installer");
   128 	if (group)
   129 	{
   130 	    packages=plover_vector_new();
   131 	    do
   132 	    {
   133 		changed=FALSE;
   134 		for(pkg=group->packages;pkg;pkg=pkg->next)
   135 		{
   136 		    if (plover_vector_contains(packages,pkg->name))
   137 			continue;
   138 		    if (pkg->type==COMPS_REQUIREMENT_DEFAULT ||
   139 		      pkg->type==COMPS_REQUIREMENT_MANDATORY ||
   140 		      pkg->type==COMPS_REQUIREMENT_CONDITIONAL &&
   141 		      plover_vector_contains(packages,pkg->requires))
   142 		    {
   143 			changed=TRUE;
   144 			plover_vector_append(packages,pkg->name);
   145 		    }
   146 		}
   147 	    } while(changed);
   148 	    pkgs=packages->strings;
   149 	}
   150     }
   151     if (!retval)
   152 	retval=!plover_install_uri(repository,prefix,pkgs,&error);
   153     if (!retval)
   154 	retval=!plover_update_uri(repository,prefix,NULL,&error);
   155     if (packages)
   156 	plover_vector_free(packages);
   157     if (comps)
   158 	plover_comps_free(comps);
   159     if (error)
   160     {
   161 	fprintf(stderr,"%s\n",error->message);
   162 	g_error_free(error);
   163     }
   164 #ifdef WIN32
   165     PostQuitMessage(retval);
   166     PostThreadMessage(main_thread_id,WM_QUIT,retval,0);
   167     _endthreadex(retval);
   168 #endif
   169     return retval;
   170 }
   171 
   172 /*
   173  * The idea is that if pre_install() fails, update/setup should fall back
   174  * to console interfaces.
   175  */
   176 #ifdef WIN32
   177 HANDLE
   178 #else
   179 void *
   180 #endif
   181 pre_install(const char *repository)
   182 {
   183 #ifdef WIN32
   184     HANDLE retval;
   185 #else
   186     void *retval;
   187 #endif
   188     razor_set_lua_loader("posix",(void (*)())luaopen_posix);
   189     razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
   190 #ifdef WIN32
   191     retval=(HANDLE)_beginthreadex(NULL,0,pre_install_thread,(void *)repository,
   192       0,NULL);
   193 #else
   194     if (pre_install_thread((void *)repository))
   195 	retval=NULL;
   196     else
   197 	retval=(void *)1;		/* Non-NULL to indicate success */
   198 #endif
   199     return retval;
   200 }
   201 
   202 #ifndef WIN32
   203 int remove_ignore(const char *fpath,const struct stat *sb,int typeflag,
   204   struct FTW *ftwbuf)
   205 {
   206     (void)remove(fpath);
   207     return 0;
   208 }
   209 #endif
   210 
   211 gboolean deltree(const char *path)
   212 {
   213 #ifdef WIN32
   214     /* Based on g_local_file_trash() */
   215     SHFILEOPSTRUCTW op={0};
   216     gboolean success;
   217     wchar_t *wfilename;
   218     long len;
   219     wfilename=g_utf8_to_utf16(path,-1,NULL,&len,NULL);
   220     /* SHFILEOPSTRUCT.pFrom is double-zero-terminated */
   221     wfilename=g_renew(wchar_t,wfilename,len+2);
   222     wfilename[len+1]=0;
   223     op.wFunc=FO_DELETE;
   224     op.pFrom=wfilename;
   225     op.fFlags=FOF_NO_UI;
   226     success=!SHFileOperationW(&op);
   227     if (success && op.fAnyOperationsAborted)
   228 	success=FALSE;
   229     g_free(wfilename);
   230     return success;
   231 #else
   232     return nftw(path,remove_ignore,64,FTW_DEPTH|FTW_PHYS);
   233 #endif
   234 }
   235 
   236 gboolean pre_uninstall(void)
   237 {
   238     gboolean success;
   239     gchar *s,*uri;
   240     GError *error=NULL;
   241     razor_set_lua_loader("posix",(void (*)())luaopen_posix);
   242     razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
   243     prefix=plover_pre_install_prefix();
   244     s=g_strconcat(prefix,"/var/lib/razor",NULL);
   245     uri=razor_path_to_uri(s);
   246     g_free(s);
   247     razor_set_database_uri(uri);
   248     free(uri);
   249     success=plover_remove(NULL,&error);
   250     if (error)
   251     {
   252 	fprintf(stderr,"%s\n",error->message);
   253 	g_error_free(error);
   254     }
   255     deltree(prefix);
   256     return success;
   257 }
   258 
   259 #if defined(WIN32) && !defined(USE_G_SPAWN)
   260 /* Based on glib's g_spawn_win32.c */
   261 
   262 static gchar *
   263 win32_cmdline_quote(const char *string)
   264 {
   265     const gchar *p=string;
   266     gchar *retval,*q;
   267     gint len=0;
   268     gboolean need_dblquotes=FALSE;
   269     while (*p)
   270     {
   271 	if (*p==' ' || *p=='\t')
   272 	    need_dblquotes=TRUE;
   273 	else if (*p=='"')
   274 	    len++;
   275 	else if (*p=='\\')
   276 	{
   277 	    const gchar *pp=p;
   278 	    while (*pp && *pp=='\\')
   279 		pp++;
   280 	    if (*pp=='"')
   281 		len++;
   282 	}
   283 	len++;
   284 	p++;
   285     }
   286     q=retval=g_malloc(len+need_dblquotes*2+1);
   287     p=string;
   288     if (need_dblquotes)
   289 	*q++='"';
   290     while (*p)
   291     {
   292 	if (*p=='"')
   293 	    *q++='\\';
   294 	else if (*p=='\\')
   295 	{
   296 	    const gchar *pp=p;
   297 	    while (*pp && *pp=='\\')
   298 		pp++;
   299 	    if (*pp=='"')
   300 		*q++='\\';
   301 	}
   302 	*q++=*p;
   303 	p++;
   304     }
   305     if (need_dblquotes)
   306 	*q++='"';
   307     *q++='\0';
   308     return retval;
   309 }
   310 
   311 /* Create a win32-style wide-character argv with suitable quoting */
   312 wchar_t **win32_argv_import(char **argv,GError **error)
   313 {
   314     int i,n;
   315     gchar *s;
   316     wchar_t **wargv;
   317     GError *tmp_error=NULL;
   318     n=g_strv_length(argv);
   319     wargv=g_new(wchar_t *,n+1);
   320     for(i=0;i<n;i++)
   321     {
   322 	s=win32_cmdline_quote(argv[i]);
   323 	wargv[i]=g_utf8_to_utf16(s,-1,NULL,NULL,&tmp_error);
   324 	g_free(s);
   325 	if (!wargv[i])
   326 	{
   327 	    g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   328 	      "Invalid argument #%d: %s",i,tmp_error->message);
   329 	    g_error_free(tmp_error);
   330 	    for(i--;i>=0;i--)
   331 		g_free(wargv[i]);
   332 	    g_free(wargv);
   333 	    return FALSE;
   334 	}
   335     }
   336     wargv[i]=NULL;
   337     return wargv;
   338 }
   339 
   340 gboolean spawn_sync(char **argv,GError **error)
   341 {
   342     wchar_t *wargv0,**wargv;
   343     gintptr rc;
   344     GError *tmp_error=NULL;
   345     wargv0=g_utf8_to_utf16(argv[0],-1,NULL,NULL,&tmp_error);
   346     if (!wargv0)
   347     {
   348 	fprintf(stderr,"Conversion error in post\n");
   349 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   350 	  "Invalid program name: %s",tmp_error->message);
   351 	g_error_free(tmp_error);
   352 	return FALSE;
   353     }
   354     wargv=win32_argv_import(argv,error);
   355     if (!wargv)
   356     {
   357 	fprintf(stderr,"Conversion error in post\n");
   358 	g_free(wargv0);
   359 	return FALSE;
   360     }
   361     errno=0;
   362     rc=_wspawnvp(P_WAIT,wargv0,(const wchar_t **)wargv);
   363     g_free(wargv0);
   364     g_strfreev((gchar **)wargv);
   365     if (rc==-1 && errno!=0)
   366     {
   367 	fprintf(stderr,"Failed to start post command (%s)\n",g_strerror(errno));
   368 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   369 	  "Failed to execute post command: %s",g_strerror(errno));
   370 	return FALSE;
   371     }
   372     if (rc!=EXIT_SUCCESS)
   373     {
   374 	fprintf(stderr,"post command failed (%ld)\n",(long)rc);
   375 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   376 	  "Post command exited with code %ld",(long)rc);
   377 	return FALSE;
   378     }
   379     return TRUE;
   380 }
   381 
   382 #endif /* defined(WIN32) && !defined(USE_G_SPAWN) */
   383 
   384 gboolean pre_install_spawn_sync(char **argv,GError **error)
   385 {
   386     GError *tmp_error=NULL;
   387 #ifdef USE_G_SPAWN
   388     gchar *standard_output,*standard_error;
   389     int exit_status;
   390     if (!g_spawn_sync(NULL,argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,
   391       &standard_output,&standard_error,&exit_status,&tmp_error))
   392     {
   393 	fprintf(stderr,"Failed to start post command\n");
   394 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   395 	return FALSE;
   396     }
   397     if (standard_output && *standard_output)
   398     {
   399 	printf("Output from post command %s:\n",argv[0]);
   400 	fputs(standard_output,stdout);
   401     }
   402     g_free(standard_output);
   403     if (standard_error && *standard_error)
   404     {
   405 	printf("Error output from post command %s:\n",argv[0]);
   406 	fputs(standard_error,stdout);
   407     }
   408     g_free(standard_error);
   409     if (!g_spawn_check_exit_status(exit_status,&tmp_error))
   410     {
   411 	fprintf(stderr,"post command failed\n");
   412 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   413 	return FALSE;
   414     }
   415 #else
   416     if (!spawn_sync(argv,&tmp_error))
   417     {
   418 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   419 	return FALSE;
   420     }
   421 #endif
   422     return TRUE;
   423 }
   424 
   425 /*
   426  * Run a command after completing request.
   427  *
   428  * Command may refer to %INSTALL_PREFIX% which will be replaced by the
   429  * (first) install prefix used, by %TEST_RESULT% which will be replaced
   430  * by either "pass" or "fail" depending as to whether the request succeeded
   431  * or not and/or by %REPOSITORY% which will be replaced by the URI of the
   432  * repository used. Command may also include double quotes which will be used
   433  * to affect how the command is split into arguments much like a shell does.
   434  */
   435 gboolean run_post(int argc,char **argv,gboolean test_result,
   436   const char *repository,GError **error)
   437 {
   438     int i,post_argc;
   439     char *s;
   440     gchar *expanded;
   441     gchar **post_argv;
   442     GError *tmp_error=NULL;
   443     if (argc<2)
   444     {
   445 	g_set_error_literal(error,G_FILE_ERROR,G_FILE_ERROR_NOENT,
   446 	  "--post: No command given");
   447 	return FALSE;
   448     }
   449     printf("Running post command: %s\n",argv[1]);
   450     if (!g_shell_parse_argv(argv[1],&post_argc,&post_argv,&tmp_error))
   451     {
   452 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[1]);
   453 	return FALSE;
   454     }
   455     for(i=0;i<post_argc;i++)
   456     {
   457 	s=strstr(post_argv[i],"%INSTALL_PREFIX%");
   458 	if (s)
   459 	{
   460 	    *s='\0';
   461 	    s+=strlen("%INSTALL_PREFIX%");
   462 	    expanded=g_strconcat(post_argv[i],prefix,s,NULL);
   463 	    g_free(post_argv[i]);
   464 	    post_argv[i]=expanded;
   465 	}
   466 	s=strstr(post_argv[i],"%TEST_RESULT%");
   467 	if (s)
   468 	{
   469 	    *s='\0';
   470 	    s+=strlen("%TEST_RESULT%");
   471 	    expanded=g_strconcat(post_argv[i],test_result?"pass":"fail",s,NULL);
   472 	    g_free(post_argv[i]);
   473 	    post_argv[i]=expanded;
   474 	}
   475 	s=strstr(post_argv[i],"%REPOSITORY%");
   476 	if (s)
   477 	{
   478 	    *s='\0';
   479 	    s+=strlen("%REPOSITORY%");
   480 	    expanded=g_strconcat(post_argv[i],repository,s,NULL);
   481 	    g_free(post_argv[i]);
   482 	    post_argv[i]=expanded;
   483 	}
   484     }
   485     return pre_install_spawn_sync(post_argv,error);
   486 }
   487 
   488 #ifdef WIN32
   489 DWORD win32_pre_install_gui(char *repository)
   490 {
   491     HANDLE thread;
   492     INITCOMMONCONTROLSEX icc={0,};
   493     MSG msg;
   494     DWORD retval;
   495     main_thread_id=GetCurrentThreadId();
   496     thread=(HANDLE)pre_install(repository);
   497     if (!thread)
   498 	return EXIT_FAILURE;
   499     icc.dwSize=sizeof(icc);
   500     icc.dwICC=ICC_WIN95_CLASSES;
   501     InitCommonControlsEx(&icc);
   502     DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_PROGRESSDIALOG),NULL,
   503       &ProgressDialogProc);
   504     while(GetMessage(&msg,NULL,0,0)>0)
   505     {
   506 	TranslateMessage(&msg);
   507 	DispatchMessage(&msg);
   508     }
   509     WaitForSingleObject(thread,INFINITE);
   510     GetExitCodeThread(thread,&retval);
   511     CloseHandle(thread);
   512     return retval;
   513 }
   514 #endif	/* WIN32 */
   515 
   516 gchar *pre_install_default_repository(const char *argv0)
   517 {
   518     size_t length;
   519     void *contents;
   520     gchar *path;
   521     gchar *s,*uri;
   522     struct razor_error *tmp_error=NULL;
   523     /*
   524      * The default repository is the executable itself if it's an archive
   525      * or otherwise the directory in which the executable is stored.
   526      */
   527     path=plover_get_program(argv0);
   528     uri=razor_path_to_uri(path);
   529     free(path);
   530     s=g_strconcat(uri,"/repodata/comps.xml",NULL);
   531     contents=razor_uri_get_contents(s,&length,FALSE,&tmp_error);
   532     g_free(s);
   533     if (contents)
   534 	razor_uri_free_contents(contents,length);
   535     else
   536     {
   537 	if (razor_error_matches(tmp_error,RAZOR_GENERAL_ERROR,
   538 	  RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE))
   539 	{
   540 	    path=plover_get_program_directory(argv0);
   541 	    uri=razor_path_to_uri(path);
   542 	    free(path);
   543 	}
   544 	razor_error_free(tmp_error);
   545     }
   546     return uri;
   547 }
   548 
   549 gboolean pre_install_post(int argc,char **argv,gboolean success,
   550   const char *repository)
   551 {
   552     gchar *s,*uri;
   553     struct post *post=NULL;
   554     GError *error=NULL;
   555     if (!argv && !success)
   556     {
   557 	/*
   558 	 * If no --post command has been given, then we want to run the
   559 	 * program specified in %INSTALL_PREFIX%/etc/pre-inst.post
   560 	 * However, if the pre-install failed, then that file isn't likely
   561 	 * to exist (and it's requirements even less so) so we issue an error
   562 	 * instead. Unfortunately, it's hard to include the error message
   563 	 * here. It will have been logged in the plover log, but that's not
   564 	 * much help to users.
   565 	 */
   566 #ifndef WIN32
   567 	fprintf(stderr,
   568 	  "Installation failed: Failed to unpack the main installer\n");
   569 #else
   570 	MessageBox(NULL,"Failed to unpack the main installer",
   571 	  "Installation failed",MB_ICONERROR|MB_OK);
   572 #endif
   573 	return FALSE;
   574     }
   575     if (!argv)
   576     {
   577 	post=pre_install_post_new(repository,prefix);
   578 	uri=razor_path_to_uri(prefix);
   579 	s=g_strconcat(uri,"/etc/pre-inst.post",NULL);
   580 	g_free(uri);
   581 	pre_install_post_load_uri(post,s,&error);
   582 	if (error)
   583 	    g_debug("Failed to load post configuration from pre-inst.post: %s",
   584 	      error->message);
   585 	g_free(s);
   586 	if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT))
   587 	{
   588 	    /*
   589 	     * If no post configuration file exists, that's not an error;
   590 	     * there simply is no post action configured.
   591 	     */
   592 	    g_clear_error(&error);
   593 	}
   594     }
   595     if (post && post->argc>0)
   596 	pre_install_spawn_sync(post->argv,&error);
   597     else if (argv)
   598 	run_post(argc,argv,success,repository,&error);
   599     if (post)
   600 	pre_install_post_free(post);
   601     if (error)
   602     {
   603 #ifndef WIN32
   604 	fprintf(stderr,"Error in post: %s\n",error->message);
   605 #else
   606 	MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK);
   607 #endif
   608 	g_error_free(error);
   609 	success=FALSE;
   610     }
   611     return success;
   612 }
   613 
   614 int main(int argc,char **argv)
   615 {
   616     gboolean success,uninstall=FALSE,enable_post=FALSE;
   617     GError *error=NULL;
   618     gchar *path=NULL,*repository=NULL;
   619     GOptionContext *context;
   620     GOptionEntry options[]={
   621 	{"repository",0,0,G_OPTION_ARG_STRING,&repository,
   622 	  "Repository location","uri"},
   623 	{"path",0,0,G_OPTION_ARG_FILENAME,&path,
   624 	  "Repository path","path"},
   625 	{"uninstall",'u',0,G_OPTION_ARG_NONE,&uninstall,
   626 	  "Uninstall all packages",NULL},
   627 	{"post",0,0,G_OPTION_ARG_NONE,&enable_post,
   628 	  "Run command after request is processed",NULL},
   629 	{NULL}
   630     };
   631 #ifdef WIN32
   632     /*
   633      * pre-inst is normally a GUI application, but rpm scripts may well
   634      * call console applications and it looks ugly if console windows keep
   635      * popping up. Avoid this by allocating our own console and hiding it.
   636      * Note:
   637      *	- If pre-inst is a console application (typically for debugging),
   638      *    then skip this step.
   639      *  - Call ShowWindow twice to negate special handling on first call.
   640      */
   641     if (!GetConsoleWindow())
   642     {
   643 	AllocConsole();
   644 	ShowWindow(GetConsoleWindow(),SW_HIDE);
   645 	ShowWindow(GetConsoleWindow(),SW_HIDE);
   646     }
   647 #endif
   648     plover_exception_handler_init();
   649     context=g_option_context_new("[command] - install the main installer");
   650     g_option_context_add_main_entries(context,options,NULL);
   651     g_option_context_set_description(context,
   652       "If --post is specified, then the command to run and its arguments\n"
   653       "should be listed at the end of the command line.\n"
   654       "Command may refer to %INSTALL_PREFIX% which will be replaced by the\n"
   655       "(first) install prefix used, by %TEST_RESULT% which will be\n"
   656       "replaced by either \"pass\" or \"fail\" depending as to whether the\n"
   657       "request succeeded or not and/or by %REPOSITORY% which will be\n"
   658       "replaced by the URI of the repository used. Command may also include\n"
   659       "double quotes which will be used to affect how the command is split\n"
   660       "into arguments much like a shell does.");
   661     g_option_context_set_strict_posix(context,TRUE);
   662     g_option_context_set_ignore_unknown_options(context,TRUE);
   663     if (!g_option_context_parse(context,&argc,&argv,&error))
   664     {
   665 	g_printerr("pre-install: %s\n",error->message);
   666 	g_printerr("Use \"%s --help\" for help\n",argv[0]);
   667 	exit(1);
   668     }
   669     if (repository && path)
   670     {
   671 	g_printerr("pre-install: "
   672 	  "Only one of --repository and --path can be specified\n");
   673 	exit(1);
   674     }
   675     if (path)
   676 	repository=razor_path_to_uri(path);
   677     else if (!repository)
   678 	repository=pre_install_default_repository(argv[0]);
   679     if (uninstall)
   680 	success=pre_uninstall();
   681     else
   682     {
   683 #ifdef WIN32
   684 	success=win32_pre_install_gui(repository)==EXIT_SUCCESS;
   685 #else
   686 	success=!!pre_install(repository);
   687 #endif
   688     }
   689     if (enable_post)
   690 	success=pre_install_post(argc,argv,success,repository);
   691     else if (!uninstall)
   692 	success=pre_install_post(0,NULL,success,repository);
   693 #ifdef WIN32
   694     return success?EXIT_SUCCESS:EXIT_FAILURE;
   695 #else
   696     return success?0:1;
   697 #endif
   698 }