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